diff --git a/.gitignore b/.gitignore index 07ddf61..ee2a18c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ imgui.ini *.txt cmake_install.cmake Config.h +Timestamp.h #Folders Debug/ diff --git a/CMakeLists.txt b/CMakeLists.txt index a82502e..f73262a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,8 +12,72 @@ project(FocalEngine) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_FLAGS_DEBUG "/MP /ZI /W3 /Od /MTd /JMC /sdl /FC /D IMGUI_IMPL_OPENGL_LOADER_GLEW /D WIN32 /D _WINDOWS /D _DEBUG /D FE_DEBUG_ENABLED") -set(CMAKE_CXX_FLAGS_RELEASE "/MP /MT /W3 /O2 /Oi /Gy /sdl /FC /GL /OPT:REF /OPT:ICF /D IMGUI_IMPL_OPENGL_LOADER_GLEW /D NDEBUG /D WIN32 /D _WINDOWS") +if(MSVC) + # Disable C4251 warning + add_compile_options(/wd4251) + + # Disable LNK4197 warning + add_link_options("/ignore:4197") +endif() + +option(ENGINE_HEADERS_ONLY "Include only engine headers" OFF) +option(ENABLE_TESTS "Enable tests" OFF) + +set(TEST_LIBS) +set(TEST_SOURCE_FILES) +set(TEST_INCLUDE_FILES) + +if(NOT TARGET FEBasicApplication) + add_subdirectory(SubSystems/FEBasicApplication) +endif() + +if(MSVC) + # Disable C4251 warning + add_compile_options(/wd4251) + + if(USE_STATIC_RUNTIME) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") + else() + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") + endif() + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") +endif() + +if(ENABLE_TESTS) + if(BUILD_SHARED_LIBS) + list(APPEND TEST_LIBS + debug Tests/ThirdParty/googletest/lib/Debug/Dynamic/gtest + debug Tests/ThirdParty/googletest/lib/Debug/Dynamic/gtest_main + optimized Tests/ThirdParty/googletest/lib/Release/Dynamic/gtest + optimized Tests/ThirdParty/googletest/lib/Release/Dynamic/gtest_main + ) + else() + list(APPEND TEST_LIBS + debug Tests/ThirdParty/googletest/lib/Debug/gtest + debug Tests/ThirdParty/googletest/lib/Debug/gtest_main + optimized Tests/ThirdParty/googletest/lib/Release/gtest + optimized Tests/ThirdParty/googletest/lib/Release/gtest_main + ) + endif() + + list(APPEND TEST_SOURCE_FILES + "Tests/RunAllTests.cpp" + "Tests/RunAllTests.h" + "Tests/SceneGraphTest.cpp" + "Tests/SceneGraphTest.h" + ) + + list(APPEND TEST_INCLUDE_FILES + "Tests/ThirdParty/googletest/include" + ) + + # Adding flags for multi-processor compilation + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MP") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MP") +endif() # Turn on the ability to create folders to organize projects (.vcproj) # It creates "CMakePredefinedTargets" folder by default and adds CMake @@ -26,10 +90,6 @@ set(ENGINE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}") set(ENGINE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE) configure_file(${ENGINE_FOLDER}/ResourceManager/Config.h.in ${ENGINE_FOLDER}/ResourceManager/Config.h @ONLY) -if(NOT TARGET FEBasicApplication) - add_subdirectory(SubSystems/FEBasicApplication) -endif() - file(GLOB InstancedLineMaterial_SRC "CoreExtensions/StandardMaterial/InstancedLineMaterial/FE_InstancedLine_FS.glsl" "CoreExtensions/StandardMaterial/InstancedLineMaterial/FE_InstancedLine_VS.glsl" @@ -134,28 +194,15 @@ file(GLOB ComputeShaders_SRC "CoreExtensions/ComputeShaders/FE_ComputeDepthPyramidDownSample_CS.glsl" ) -file(GLOB CoreExtensions_SRC - "CoreExtensions/FEFreeCamera.cpp" - "CoreExtensions/FEFreeCamera.h" - "CoreExtensions/FEModelViewCamera.cpp" - "CoreExtensions/FEModelViewCamera.h" -) - # *************** RENDERER *************** file(GLOB Renderer_SRC - "Renderer/FEEntity.cpp" - "Renderer/FEEntity.h" - "Renderer/FEEntityInstanced.cpp" - "Renderer/FEEntityInstanced.h" - "Renderer/FEPrefab.cpp" - "Renderer/FEPrefab.h" + "Renderer/FEViewport.cpp" + "Renderer/FEViewport.h" "Renderer/FEFramebuffer.cpp" "Renderer/FEFramebuffer.h" "Renderer/FEGameModel.cpp" "Renderer/FEGameModel.h" - "Renderer/FELight.cpp" - "Renderer/FELight.h" "Renderer/FELine.cpp" "Renderer/FELine.h" "Renderer/FEMaterial.cpp" @@ -169,10 +216,11 @@ file(GLOB Renderer_SRC "Renderer/FEShader.cpp" "Renderer/FEShader.h" "Renderer/FEShader.inl" + "Renderer/FEShaderUniform.cpp" + "Renderer/FEShaderUniform.h" + "Renderer/FEShaderUniform.inl" "Renderer/FETexture.cpp" "Renderer/FETexture.h" - "Renderer/FETerrain.cpp" - "Renderer/FETerrain.h" ) # *************** RESOURCE MANAGER *************** @@ -183,6 +231,7 @@ file(GLOB ResourceManager_SRC "ResourceManager/FEglTFLoader.cpp" "ResourceManager/FEglTFLoader.h" "ResourceManager/FEResourceManager.cpp" + "ResourceManager/FEResourceManager.inl" "ResourceManager/FEResourceManager.h" "ResourceManager/Config.h" ) @@ -190,6 +239,7 @@ file(GLOB ResourceManager_SRC # *************** CORE *************** file(GLOB Core_SRC + "Core/FocalEngineAPI.h" "Core/FECoreIncludes.h" "Core/FEGeometricTools.cpp" "Core/FEGeometricTools.h" @@ -197,24 +247,109 @@ file(GLOB Core_SRC "Core/FEObject.h" ) +# *************** COMPONENTS *************** + +file(GLOB Scene_Components_SRC + "SubSystems/Scene/Components/FEComponents.h" + "SubSystems/Scene/Components/FEComponents.cpp" + "SubSystems/Scene/Components/FETagComponent.cpp" + "SubSystems/Scene/Components/FETagComponent.h" + "SubSystems/Scene/Components/FETransformComponent.cpp" + "SubSystems/Scene/Components/FETransformComponent.h" + "SubSystems/Scene/Components/FECameraComponent.cpp" + "SubSystems/Scene/Components/FECameraComponent.h" + "SubSystems/Scene/Components/FELightComponent.cpp" + "SubSystems/Scene/Components/FELightComponent.h" + "SubSystems/Scene/Components/FEGameModelComponent.cpp" + "SubSystems/Scene/Components/FEGameModelComponent.h" + "SubSystems/Scene/Components/FEInstancedComponent.cpp" + "SubSystems/Scene/Components/FEInstancedComponent.h" + "SubSystems/Scene/Components/FETerrainComponent.cpp" + "SubSystems/Scene/Components/FETerrainComponent.h" + "SubSystems/Scene/Components/FESkyDomeComponent.cpp" + "SubSystems/Scene/Components/FESkyDomeComponent.h" + "SubSystems/Scene/Components/FEPrefabInstanceComponent.h" + "SubSystems/Scene/Components/FEPrefabInstanceComponent.cpp" + "SubSystems/Scene/Components/FEVirtualUIComponent.h" + "SubSystems/Scene/Components/FEVirtualUIComponent.cpp" +) + +file(GLOB Scene_Component_Systems_SRC + "SubSystems/Scene/Components/Systems/FEComponentSystems.h" + "SubSystems/Scene/Components/Systems/FETransformSystem.h" + "SubSystems/Scene/Components/Systems/FETransformSystem.cpp" + "SubSystems/Scene/Components/Systems/FELightSystem.h" + "SubSystems/Scene/Components/Systems/FELightSystem.cpp" + "SubSystems/Scene/Components/Systems/FECameraSystem.h" + "SubSystems/Scene/Components/Systems/FECameraSystem.cpp" + "SubSystems/Scene/Components/Systems/FEInstancedSystem.h" + "SubSystems/Scene/Components/Systems/FEInstancedSystem.cpp" + "SubSystems/Scene/Components/Systems/FETerrainSystem.h" + "SubSystems/Scene/Components/Systems/FETerrainSystem.cpp" + "SubSystems/Scene/Components/Systems/FESkyDomeSystem.h" + "SubSystems/Scene/Components/Systems/FESkyDomeSystem.cpp" + "SubSystems/Scene/Components/Systems/FEPrefabInstanceSystem.h" + "SubSystems/Scene/Components/Systems/FEPrefabInstanceSystem.cpp" + "SubSystems/Scene/Components/Systems/FEVirtualUISystem.h" + "SubSystems/Scene/Components/Systems/FEVirtualUISystem.cpp" +) + +file(GLOB Scene_NativeScript_System_SRC + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptCore.h" + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptCore.cpp" + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptModule.h" + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptModule.cpp" + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.h" + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.inl" + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.cpp" + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.h" + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.inl" + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.cpp" + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptProject.h" + "SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptProject.cpp" +) + +set(Unpacked_NativeScript_Connector_SRC) + +if(USE_STATIC_RUNTIME) + list(APPEND Unpacked_NativeScript_Connector_SRC + "Resources/UserScriptsData/FENativeScriptConnector.h" + "Resources/UserScriptsData/FENativeScriptConnector.cpp" + ) +endif() + # *************** SCENE *************** file(GLOB Scene_SRC + "SubSystems/Scene/FESceneManager.cpp" + "SubSystems/Scene/FESceneManager.inl" + "SubSystems/Scene/FESceneManager.h" "SubSystems/Scene/FEScene.cpp" + "SubSystems/Scene/FEScene.inl" "SubSystems/Scene/FEScene.h" - "SubSystems/Scene/FETransformComponent.cpp" - "SubSystems/Scene/FETransformComponent.h" + "SubSystems/Scene/FENaiveSceneGraph.cpp" + "SubSystems/Scene/FENaiveSceneGraph.inl" + "SubSystems/Scene/FENaiveSceneGraph.h" + "SubSystems/Scene/FENaiveSceneGraphNode.cpp" + "SubSystems/Scene/FENaiveSceneGraphNode.h" + "SubSystems/Scene/FEEntity.cpp" + "SubSystems/Scene/FEEntity.h" + "SubSystems/Scene/FEPrefab.cpp" + "SubSystems/Scene/FEPrefab.h" ) # *************** SUB_SYSTEM *************** +file(GLOB FileSystem_SubSystems_SRC + "SubSystems/FileSystem/FEFileSystem.cpp" + "SubSystems/FileSystem/FEFileSystem.h" + "SubSystems/FileSystem/FEAssetPackage.cpp" + "SubSystems/FileSystem/FEAssetPackage.h" +) + file(GLOB SubSystems_SRC - "SubSystems/FEBasicCamera.cpp" - "SubSystems/FEBasicCamera.h" - "SubSystems/FEFileSystem.cpp" - "SubSystems/FEFileSystem.h" - "SubSystems/FEVirtualUIContext.h" - "SubSystems/FEVirtualUIContext.cpp" + "SubSystems/FEInput.cpp" + "SubSystems/FEInput.h" ) file(GLOB OpenXR_SRC @@ -255,8 +390,9 @@ file(GLOB source_SRC link_directories(${GLEW_LIB_DIR}) link_directories(${GLFW_LIB_DIR}) -# add the executable -add_library(FocalEngine +set(ALL_SOURCE_FILES "") +list(APPEND ALL_SOURCE_FILES + ${TEST_SOURCE_FILES} ${source_SRC} ${InstancedLineMaterial_SRC} ${PBRMaterial_SRC} @@ -276,14 +412,17 @@ add_library(FocalEngine ${FE_SSAO_SRC} ${ComputeShaders_SRC} ${Core_SRC} - ${CoreExtensions_SRC} # *************** RENDERER *************** ${Renderer_SRC} # *************** RESOURCE MANAGER *************** ${ResourceManager_SRC} # *************** SUB_SYSTEM *************** ${SubSystems_SRC} + ${FileSystem_SubSystems_SRC} ${Scene_SRC} + ${Scene_Components_SRC} + ${Scene_Component_Systems_SRC} + ${Scene_NativeScript_System_SRC} ${OpenXR_SRC} # *************** THIRD_PARTY *************** ${jsoncpp_SRC} @@ -291,17 +430,124 @@ add_library(FocalEngine ${stb_image_SRC} ) -target_link_libraries(FocalEngine - PRIVATE - FEBasicApplication - glew32s.lib - glfw3.lib - glfw3dll.lib - opengl32.lib - PUBLIC - debug ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Debug/openxr_loaderd.lib - optimized ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Release/openxr_loader.lib +if(USE_STATIC_RUNTIME) + list(APPEND ALL_SOURCE_FILES + ${Unpacked_NativeScript_Connector_SRC} + ) +endif() + +if(ENABLE_TESTS) + add_executable(FocalEngine WIN32 ${ALL_SOURCE_FILES}) +else() + if(BUILD_SHARED_LIBS) + if(ENGINE_HEADERS_ONLY) + add_library(FocalEngine INTERFACE) + target_include_directories(FocalEngine INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} + ${GLEW_INCLUDE_DIR} + ${BASICAPP_THIRDPARTY_DIR} + ${BASICAPP_DIR} + ${FOCAL_ENGINE_INCLUDES} + ) + else() + add_library(FocalEngine SHARED ${ALL_SOURCE_FILES}) + target_compile_definitions(FocalEngine PRIVATE FOCAL_ENGINE_EXPORTS) + target_compile_definitions(FocalEngine PRIVATE FEBASICAPPLICATION_SHARED) + target_compile_definitions(FocalEngine PUBLIC FOCAL_ENGINE_SHARED) + + # Sometimes /GL conflicts with WINDOWS_EXPORT_ALL_SYMBOLS + set_target_properties(FocalEngine PROPERTIES COMPILE_OPTIONS "/GL-") + set_target_properties(FocalEngine PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) + + # Disable C4251 warning + target_compile_options(FocalEngine PRIVATE /wd4251) + endif() + else() + add_library(FocalEngine STATIC ${ALL_SOURCE_FILES}) + endif() +endif() + +# Adjust OpenXRLoader linking based on BUILD_SHARED_LIBS and FEBASICAPPLICATION_STATIC_RUNTIME +if(BUILD_SHARED_LIBS) + if(USE_STATIC_RUNTIME) + message(WARNING "Building a shared library with static runtime is uncommon and may lead to issues.") + set(OpenXRLoader_Debug ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.lib) + set(OpenXRLoader_Release ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Release/DLL/Static_runtime/openxr_loader.lib) + + set(OpenXRLoader_DLL_Debug ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.dll) + set(OpenXRLoader_DLL_Release ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Release/DLL/Static_runtime/openxr_loader.dll) + else() + set(OpenXRLoader_Debug ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.lib) + set(OpenXRLoader_Release ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.lib) + + set(OpenXRLoader_DLL_Debug ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.dll) + set(OpenXRLoader_DLL_Release ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.dll) + endif() +else() + if(USE_STATIC_RUNTIME) + set(OpenXRLoader_Debug ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Debug/Static_runtime/openxr_loaderd.lib) + set(OpenXRLoader_Release ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Release/Static_runtime/openxr_loader.lib) + + # No DLL for static library + set(OpenXRLoader_DLL_Debug "") + set(OpenXRLoader_DLL_Release "") + else() + set(OpenXRLoader_Debug ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Debug/Dynamic_runtime/openxr_loaderd.lib) + set(OpenXRLoader_Release ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Release/Dynamic_runtime/openxr_loader.lib) + + set(OpenXRLoader_DLL_Debug ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.dll) + set(OpenXRLoader_DLL_Release ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.dll) + endif() +endif() + +# Set OpenXRLoader_DLL based on the current configuration +set(OpenXRLoader_DLL $<$:${OpenXRLoader_DLL_Debug}>$<$:${OpenXRLoader_DLL_Release}>) + +# Define GLEW_STATIC for static builds +if(USE_STATIC_RUNTIME) + add_definitions(-DGLEW_STATIC) +endif() + +set(FOCAL_ENGINE_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/ PARENT_SCOPE) +set(FOCAL_ENGINE_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/) + +set(FOCAL_ENGINE_ENTT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/entt PARENT_SCOPE) +set(FOCAL_ENGINE_ENTT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/entt) + +set(FOCAL_ENGINE_INCLUDES "") +list(APPEND FOCAL_ENGINE_INCLUDES + ${FOCAL_ENGINE_THIRD_PARTY_DIR} + ${FOCAL_ENGINE_ENTT_DIR} ) +set(FOCAL_ENGINE_INCLUDES ${FOCAL_ENGINE_INCLUDES} PARENT_SCOPE) + +if(NOT ENGINE_HEADERS_ONLY) + target_link_libraries(FocalEngine + PRIVATE + FEBasicApplication + ${GLFW_LIBRARY} + ${GLEW_LIBRARY} + opengl32.lib + PUBLIC + debug ${OpenXRLoader_Debug} + optimized ${OpenXRLoader_Release} + ${TEST_LIBS} + ) + + target_include_directories(FocalEngine + PUBLIC + ${GLEW_INCLUDE_DIR} + ${BASICAPP_THIRDPARTY_DIR} + ${BASICAPP_DIR} + ${FOCAL_ENGINE_INCLUDES} + PRIVATE + ${TEST_INCLUDE_FILES} + ) +endif() + +if(ENABLE_TESTS) + source_group("Test Source Files" FILES ${TEST_SOURCE_FILES}) +endif() source_group("Source Files" FILES ${source_SRC}) source_group("Source Files/Core" FILES ${Core_SRC}) @@ -322,14 +568,20 @@ source_group("Source Files/CoreExtensions/PostProcessEffects/FE_FXAA" FILES ${FE source_group("Source Files/CoreExtensions/PostProcessEffects/FE_GammaAndHDRCorrection" FILES ${FE_GammaAndHDRCorrection_SRC}) source_group("Source Files/CoreExtensions/PostProcessEffects/FE_SSAO" FILES ${FE_SSAO_SRC}) source_group("Source Files/CoreExtensions/ComputeShaders/" FILES ${ComputeShaders_SRC}) -source_group("Source Files/CoreExtensions" FILES ${CoreExtensions_SRC}) # *************** RENDERER *************** source_group("Source Files/Renderer" FILES ${Renderer_SRC}) # *************** RESOURCE MANAGER *************** source_group("Source Files/ResourceManager" FILES ${ResourceManager_SRC}) # *************** SUB_SYSTEM *************** source_group("Source Files/SubSystems" FILES ${SubSystems_SRC}) +source_group("Source Files/SubSystems/FileSystem" FILES ${FileSystem_SubSystems_SRC}) source_group("Source Files/SubSystems/Scene" FILES ${Scene_SRC}) +source_group("Source Files/SubSystems/Scene/Components" FILES ${Scene_Components_SRC}) +source_group("Source Files/SubSystems/Scene/Components/Systems" FILES ${Scene_Component_Systems_SRC}) +source_group("Source Files/SubSystems/Scene/NativeScriptSystem" FILES ${Scene_NativeScript_System_SRC}) +if(USE_STATIC_RUNTIME) + source_group("Source Files/SubSystems/Scene/NativeScriptSystem/Connector" FILES ${Unpacked_NativeScript_Connector_SRC}) +endif() source_group("Source Files/SubSystems/FEOpenXR" FILES ${OpenXR_SRC}) # *************** THIRD_PARTY *************** source_group("Source Files/ThirdParty/lodepng" FILES ${lodepng_SRC}) @@ -339,16 +591,6 @@ source_group("Source Files/ThirdParty/jsoncpp" FILES ${jsoncpp_SRC}) # set the startup project set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT FocalEngine) -set(FOCAL_ENGINE_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/ PARENT_SCOPE) -set(FOCAL_ENGINE_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/) - -include_directories( - ${GLEW_INCLUDE_DIR} - ${BASICAPP_THIRDPARTY_DIR} - ${BASICAPP_DIR} - ${FOCAL_ENGINE_THIRD_PARTY_DIR} -) - # Re-export of the variables from child projects set(GLEW_LIB_DIR ${GLEW_LIB_DIR} PARENT_SCOPE) set(GLFW_LIB_DIR ${GLFW_LIB_DIR} PARENT_SCOPE) @@ -356,3 +598,50 @@ set(GLFW_LIB_DIR ${GLFW_LIB_DIR} PARENT_SCOPE) set(GLEW_INCLUDE_DIR ${GLEW_INCLUDE_DIR} PARENT_SCOPE) set(BASICAPP_THIRDPARTY_DIR ${BASICAPP_THIRDPARTY_DIR} PARENT_SCOPE) set(BASICAPP_DIR ${BASICAPP_DIR} PARENT_SCOPE) + +# Export the BUILD_SHARED_LIBS and USE_STATIC_RUNTIME variables +set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS} PARENT_SCOPE) +set(USE_STATIC_RUNTIME ${USE_STATIC_RUNTIME} PARENT_SCOPE) + +# Export the GLEW_LIBRARY and GLFW_LIBRARY variables +set(GLEW_LIBRARY ${GLEW_LIBRARY} PARENT_SCOPE) +set(GLFW_LIBRARY ${GLFW_LIBRARY} PARENT_SCOPE) + +# Export the OpenXRLoader variable +set(OpenXRLoader_DLL ${OpenXRLoader_DLL} PARENT_SCOPE) + +# Add this line to define the .lib version info +target_compile_definitions(FocalEngine PRIVATE + ENGINE_LIB_VERSION="${ENGINE_BUILD_TIMESTAMP}" +) + +if(NOT ENGINE_HEADERS_ONLY) + # If building as DLL, copy necessary DLLs to output directory + if(BUILD_SHARED_LIBS) + add_custom_command(TARGET FocalEngine POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${GLFW_LIB_DIR}/glfw3.dll" + "$" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$/FEBasicApplication.dll" + "$" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${OpenXRLoader_DLL} + "$" + ) + endif() + + # If using dynamic runtime, copy necessary DLLs to output directory + if(NOT USE_STATIC_RUNTIME) + add_custom_command(TARGET FocalEngine POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${GLEW_LIB_DIR}/glew32.dll" + "$" + ) + endif() + + add_custom_command(TARGET FocalEngine PRE_BUILD + COMMAND ${CMAKE_COMMAND} -D ENGINE_FOLDER=${ENGINE_FOLDER} + -P ${CMAKE_CURRENT_SOURCE_DIR}/UpdateTimestamp.cmake + ) +endif() \ No newline at end of file diff --git a/Core/FECoreIncludes.h b/Core/FECoreIncludes.h index 4e18c82..0e54036 100644 --- a/Core/FECoreIncludes.h +++ b/Core/FECoreIncludes.h @@ -3,7 +3,7 @@ #pragma warning (disable: 4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) in lodepng.cpp #include "FEBasicApplication.h" - +#include "FocalEngineAPI.h" #include #include #include @@ -19,6 +19,7 @@ #include "glm/gtc/matrix_transform.hpp" #include "glm/gtc/type_ptr.inl" #include "glm/gtx/quaternion.hpp" +#include "glm/gtx/matrix_decompose.hpp" #include "jsoncpp/json/json.h" @@ -27,29 +28,33 @@ #include #define ANGLE_TORADIANS_COF glm::pi() / 180.0f +#define RADIANS_TOANGLE_COF 180.0f / glm::pi() #ifdef FE_DEBUG_ENABLED - #define FE_GL_ERROR(glCall) \ - { \ - glCall; \ - GLenum error = glGetError(); \ - if (error != 0) \ - { \ - assert("FE_GL_ERROR" && 0); \ - } \ - } -#else - #define FE_GL_ERROR(glCall) \ - { \ - glCall; \ + #define FE_GL_ERROR(GLCall) \ + { \ + GLCall; \ + GLenum Error = glGetError(); \ + if (Error != GL_NO_ERROR) \ + { \ + std::string ErrorMessage = "OpenGL Error: " + std::to_string(Error) + " at " + __FILE__ + ":" + std::to_string(__LINE__); \ + LOG.Add(ErrorMessage, "FE_GL_ERROR", FE_LOG_ERROR); \ + assert(ErrorMessage.c_str() && 0); \ + } \ + } +#else + #define FE_GL_ERROR(GLCall) \ + { \ + GLCall; \ } #endif // FE_GL_ERROR #define FE_MAX_LIGHTS 10 #define FE_CSM_UNIT 28 #define FE_WIN_32 -#define FE_MESH_VERSION 0.01f -#define FE_TEXTURE_VERSION 0.01f +#define FE_MESH_VERSION 0.02f +#define FE_TEXTURE_VERSION 0.02f +#define FE_NATIVE_SCRIPT_MODULE_VERSION 0.01f #define FE_SIMD_ENABLED diff --git a/Core/FEGeometricTools.cpp b/Core/FEGeometricTools.cpp index f30edf0..f54a375 100644 --- a/Core/FEGeometricTools.cpp +++ b/Core/FEGeometricTools.cpp @@ -1,6 +1,13 @@ #include "FEGeometricTools.h" using namespace FocalEngine; -FEGeometry* FEGeometry::Instance = nullptr; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetGeometry() +{ + return FEGeometry::GetInstancePointer(); +} +#endif + FEGeometry::FEGeometry() {} FEGeometry::~FEGeometry() {} @@ -556,6 +563,241 @@ float FEAABB::GetVolume() return Size.x * Size.y * Size.z; } +bool FEGeometry::IsEpsilonEqual(const glm::dvec3& FirstVector, const glm::dvec3& SecondVector, double Epsilon) +{ + return std::abs(FirstVector.x - SecondVector.x) < Epsilon && + std::abs(FirstVector.y - SecondVector.y) < Epsilon && + std::abs(FirstVector.z - SecondVector.z) < Epsilon; +} + +bool FEGeometry::IsEpsilonEqual(const glm::dquat& FirstQuaternion, const glm::dquat& SecondQuaternion, double Epsilon) +{ + return glm::abs(glm::dot(FirstQuaternion, SecondQuaternion)) > (1.0 - Epsilon); +} + +bool FEGeometry::IsEpsilonEqual(const glm::dmat4& FirstMatrix, const glm::dmat4& SecondMatrix, double Epsilon) +{ + return IsEpsilonEqual(FirstMatrix[0], SecondMatrix[0], Epsilon) && + IsEpsilonEqual(FirstMatrix[1], SecondMatrix[1], Epsilon) && + IsEpsilonEqual(FirstMatrix[2], SecondMatrix[2], Epsilon) && + IsEpsilonEqual(FirstMatrix[3], SecondMatrix[3], Epsilon); +} + +bool FEGeometry::DecomposeMatrixToTranslationRotationScale(const glm::dmat4& Matrix, glm::dvec3& OutTranslation, glm::dquat& OutRotationQuaternion, glm::dvec3& OutScale) +{ + // In rare cases glm::decompose can fail because of precision issues + // So we will use double precision version of glm::decompose + glm::dvec3 DoubleSkew; + glm::dvec4 DoublePerspective; + return glm::decompose(Matrix, OutScale, OutRotationQuaternion, OutTranslation, DoubleSkew, DoublePerspective); +} + +glm::vec3 FEGeometry::CalculateNormal(glm::dvec3 FirstVertex, glm::dvec3 SecondVertex, glm::dvec3 ThirdVertex) +{ + glm::dvec3 Edge_0 = ThirdVertex - SecondVertex; + glm::dvec3 Edge_1 = ThirdVertex - FirstVertex; + + glm::dvec3 Normal = glm::normalize(glm::cross(Edge_1, Edge_0)); + + if (isnan(Normal.x) || isnan(Normal.y) || isnan(Normal.z)) + Normal = glm::dvec3(); + + return Normal; +} + +void FEGeometry::CalculateNormals(const std::vector& Indices, const std::vector& Vertices, std::vector& NormalsToFill) +{ + std::vector VerticesDouble; + VerticesDouble.resize(Vertices.size()); + for (size_t i = 0; i < Vertices.size(); i++) + VerticesDouble[i] = static_cast(Vertices[i]); + + CalculateNormals(Indices, VerticesDouble, NormalsToFill); +} + +void FEGeometry::CalculateNormals(const std::vector& Indices, const std::vector& Vertices, std::vector& NormalsToFill) +{ + std::vector TrianglesArea; + std::vector TrianglePoints; + TrianglePoints.resize(3); + + for (size_t i = 0; i < Indices.size(); i += 3) + { + int VertexPosition = Indices[i] * 3; + TrianglePoints[0] = glm::dvec3(Vertices[VertexPosition], Vertices[VertexPosition + 1], Vertices[VertexPosition + 2]); + + VertexPosition = Indices[i + 1] * 3; + TrianglePoints[1] = glm::dvec3(Vertices[VertexPosition], Vertices[VertexPosition + 1], Vertices[VertexPosition + 2]); + + VertexPosition = Indices[i + 2] * 3; + TrianglePoints[2] = glm::dvec3(Vertices[VertexPosition], Vertices[VertexPosition + 1], Vertices[VertexPosition + 2]); + + TrianglesArea.push_back(CalculateTriangleArea(TrianglePoints[0], TrianglePoints[1], TrianglePoints[2])); + } + + struct VertexNormalsInfo + { + std::vector Normals; + std::vector Areas; + double AreaSum = 0.0; + }; + + std::vector DataForWeightedNormals; + DataForWeightedNormals.resize(Indices.size()); + + int IndexShift = 3; + // We assume that there were no normals info read. + for (size_t i = 0; i < Indices.size(); i += 3) + { + glm::dvec3 V0 = { Vertices[Indices[i] * IndexShift], Vertices[Indices[i] * IndexShift + 1], Vertices[Indices[i] * IndexShift + 2] }; + glm::dvec3 V1 = { Vertices[Indices[i + 1] * IndexShift], Vertices[Indices[i + 1] * IndexShift + 1], Vertices[Indices[i + 1] * IndexShift + 2] }; + glm::dvec3 V2 = { Vertices[Indices[i + 2] * IndexShift], Vertices[Indices[i + 2] * IndexShift + 1], Vertices[Indices[i + 2] * IndexShift + 2] }; + + glm::vec3 Normal = CalculateNormal(V0, V1, V2); + + DataForWeightedNormals[Indices[i]].Normals.push_back(Normal); + DataForWeightedNormals[Indices[i]].Areas.push_back(TrianglesArea[i / 3]); + DataForWeightedNormals[Indices[i]].AreaSum += TrianglesArea[i / 3]; + + DataForWeightedNormals[Indices[i + 1]].Normals.push_back(Normal); + DataForWeightedNormals[Indices[i + 1]].Areas.push_back(TrianglesArea[i / 3]); + DataForWeightedNormals[Indices[i + 1]].AreaSum += TrianglesArea[i / 3]; + + DataForWeightedNormals[Indices[i + 2]].Normals.push_back(Normal); + DataForWeightedNormals[Indices[i + 2]].Areas.push_back(TrianglesArea[i / 3]); + DataForWeightedNormals[Indices[i + 2]].AreaSum += TrianglesArea[i / 3]; + } + + for (size_t i = 0; i < Indices.size(); i += 3) + { + glm::vec3 Normal = glm::vec3(0.0f); + for (size_t j = 0; j < DataForWeightedNormals[Indices[i]].Normals.size(); j++) + { + Normal += DataForWeightedNormals[Indices[i]].Normals[j] * DataForWeightedNormals[Indices[i]].Areas[j] / DataForWeightedNormals[Indices[i]].AreaSum; + } + Normal = glm::normalize(Normal); + if (isnan(Normal.x) || isnan(Normal.y) || isnan(Normal.z)) + Normal = glm::vec3(); + + NormalsToFill[Indices[i] * IndexShift] = Normal.x; + NormalsToFill[Indices[i] * IndexShift + 1] = Normal.y; + NormalsToFill[Indices[i] * IndexShift + 2] = Normal.z; + + Normal = glm::vec3(0.0f); + for (size_t j = 0; j < DataForWeightedNormals[Indices[i + 1]].Normals.size(); j++) + { + Normal += DataForWeightedNormals[Indices[i + 1]].Normals[j] * DataForWeightedNormals[Indices[i + 1]].Areas[j] / DataForWeightedNormals[Indices[i + 1]].AreaSum; + } + Normal = glm::normalize(Normal); + if (isnan(Normal.x) || isnan(Normal.y) || isnan(Normal.z)) + Normal = glm::vec3(); + + NormalsToFill[Indices[i + 1] * IndexShift] = Normal.x; + NormalsToFill[Indices[i + 1] * IndexShift + 1] = Normal.y; + NormalsToFill[Indices[i + 1] * IndexShift + 2] = Normal.z; + + Normal = glm::vec3(0.0f); + for (size_t j = 0; j < DataForWeightedNormals[Indices[i + 2]].Normals.size(); j++) + { + Normal += DataForWeightedNormals[Indices[i + 2]].Normals[j] * DataForWeightedNormals[Indices[i + 2]].Areas[j] / DataForWeightedNormals[Indices[i + 2]].AreaSum; + } + Normal = glm::normalize(Normal); + if (isnan(Normal.x) || isnan(Normal.y) || isnan(Normal.z)) + Normal = glm::vec3(); + + NormalsToFill[Indices[i + 2] * IndexShift] = Normal.x; + NormalsToFill[Indices[i + 2] * IndexShift + 1] = Normal.y; + NormalsToFill[Indices[i + 2] * IndexShift + 2] = Normal.z; + } +} + +glm::vec3 FEGeometry::CalculateTangent(const glm::vec3 FirstVertex, const glm::vec3 SecondVertex, const glm::vec3 ThirdVertex, std::vector&& TextureCoordinates) +{ + const glm::vec3 Q1 = SecondVertex - FirstVertex; + const glm::vec3 Q2 = ThirdVertex - FirstVertex; + const glm::vec2 UV0 = TextureCoordinates[0]; + const glm::vec2 UV1 = TextureCoordinates[1]; + const glm::vec2 UV2 = TextureCoordinates[2]; + + const float T1 = UV1.y - UV0.y; + const float T2 = UV2.y - UV0.y; + + const glm::vec3 Tangent = T1 * Q2 - T2 * Q1; + return Tangent; +} + +void FEGeometry::CalculateTangents(const std::vector& Indices, const std::vector& Vertices, const std::vector& TextureCoordinates, const std::vector& Normals, std::vector& TangentsToFill) +{ + std::vector VerticesDouble; + VerticesDouble.resize(Vertices.size()); + for (size_t i = 0; i < Vertices.size(); i++) + VerticesDouble[i] = static_cast(Vertices[i]); + + CalculateTangents(Indices, VerticesDouble, TextureCoordinates, Normals, TangentsToFill); +} + +void FEGeometry::CalculateTangents(const std::vector& Indices, const std::vector& Vertices, const std::vector& TextureCoordinates, const std::vector& Normals, std::vector& TangentsToFill) +{ + for (size_t i = 0; i < Indices.size() - 1; i += 3) + { + const glm::vec3 V0 = { Vertices[Indices[i] * 3], Vertices[Indices[i] * 3 + 1], Vertices[Indices[i] * 3 + 2] }; + const glm::vec3 V1 = { Vertices[Indices[i + 1] * 3], Vertices[Indices[i + 1] * 3 + 1], Vertices[Indices[i + 1] * 3 + 2] }; + const glm::vec3 V2 = { Vertices[Indices[i + 2] * 3], Vertices[Indices[i + 2] * 3 + 1], Vertices[Indices[i + 2] * 3 + 2] }; + + glm::vec2 T0 = { TextureCoordinates[Indices[i] * 2], TextureCoordinates[Indices[i] * 2 + 1] }; + glm::vec2 T1 = { TextureCoordinates[Indices[i + 1] * 2], TextureCoordinates[Indices[i + 1] * 2 + 1] }; + glm::vec2 T2 = { TextureCoordinates[Indices[i + 2] * 2], TextureCoordinates[Indices[i + 2] * 2 + 1] }; + + glm::vec3 Tangent = GEOMETRY.CalculateTangent(V0, V1, V2, { T0, T1, T2 }); + // To eliminate NaN values after normalization. + // I encounter this problem if triangle has same texture coordinates. + if (Tangent.x != 0 || Tangent.y != 0 || Tangent.z != 0) + { + Tangent = glm::normalize(Tangent); + } + else + { + glm::vec3 Normal = { Normals[Indices[i] * 3], Normals[Indices[i] * 3 + 1], Normals[Indices[i] * 3 + 2] }; + glm::vec3 TangentOne = glm::cross(Normal, glm::vec3(0.0f, 0.0f, 1.0f)); + glm::vec3 TangentTwo = glm::cross(Normal, glm::vec3(0.0f, 1.0f, 0.0f)); + // Choosing candidate with bigger length/magnitude. + // Length/magnitude of cross product depend on sine of angle between vectors + // and sine of 90 degrees is 1.0(max value), so basically we are choosing cross product in which vectors was closer to perpendicular(assuming both vectors are unit vectors). + Tangent = glm::length(TangentOne) > glm::length(TangentTwo) ? TangentOne : TangentTwo; + Tangent = glm::normalize(Tangent); + } + + TangentsToFill[Indices[i] * 3] = Tangent.x; + TangentsToFill[Indices[i] * 3 + 1] = Tangent.y; + TangentsToFill[Indices[i] * 3 + 2] = Tangent.z; + + TangentsToFill[Indices[i + 1] * 3] = Tangent.x; + TangentsToFill[Indices[i + 1] * 3 + 1] = Tangent.y; + TangentsToFill[Indices[i + 1] * 3 + 2] = Tangent.z; + + TangentsToFill[Indices[i + 2] * 3] = Tangent.x; + TangentsToFill[Indices[i + 2] * 3 + 1] = Tangent.y; + TangentsToFill[Indices[i + 2] * 3 + 2] = Tangent.z; + } +} + +bool FEGeometry::RaysIntersection(const glm::vec3& FirstRayOrigin, const glm::vec3& FirstRayDirection, const glm::vec3& SecondRayOrigin, const glm::vec3& SecondRayDirection, float& FirstRayParametricIntersection, float& SecondRayParametricIntersection) const +{ + const glm::vec3 DirectionsCrossProduct = glm::cross(FirstRayDirection, SecondRayDirection); + + // Two rays are parallel. + if (DirectionsCrossProduct == glm::vec3(0.0f)) + return false; + + FirstRayParametricIntersection = glm::dot(glm::cross((SecondRayOrigin - FirstRayOrigin), SecondRayDirection), DirectionsCrossProduct); + FirstRayParametricIntersection /= glm::length(DirectionsCrossProduct) * glm::length(DirectionsCrossProduct); + + SecondRayParametricIntersection = glm::dot(glm::cross((SecondRayOrigin - FirstRayOrigin), FirstRayDirection), DirectionsCrossProduct); + SecondRayParametricIntersection /= glm::length(DirectionsCrossProduct) * glm::length(DirectionsCrossProduct); + + return true; +} + bool FEGeometry::IsRayIntersectingTriangle(glm::vec3 RayOrigin, glm::vec3 RayDirection, std::vector& TriangleVertices, float& Distance, glm::vec3* HitPoint, float* U, float* V) { // Ensure the triangle is defined by exactly three vertices @@ -1101,4 +1343,21 @@ std::vector FEGeometry::GetIntersectionPoints(FEAABB& AABB, std::vec } return Result; +} + +glm::dvec3 FEGeometry::CreateMouseRayToWorld(const double MouseScreenX, const double MouseScreenY, const glm::dmat4 ViewMatrix, const glm::dmat4 ProjectionMatrix, const glm::ivec2 ViewportPosition, const glm::ivec2 ViewportSize) const +{ + glm::dvec2 NormalizedMouseCoordinates; + NormalizedMouseCoordinates.x = (2.0f * (MouseScreenX - ViewportPosition.x)) / ViewportSize.x - 1; + NormalizedMouseCoordinates.y = 1.0f - (2.0f * ((MouseScreenY - ViewportPosition.y))) / ViewportSize.y; + + const glm::dvec4 ClipCoordinates = glm::dvec4(NormalizedMouseCoordinates.x, NormalizedMouseCoordinates.y, -1.0, 1.0); + glm::dvec4 EyeCoordinates = glm::inverse(ProjectionMatrix) * ClipCoordinates; + EyeCoordinates.z = -1.0f; + EyeCoordinates.w = 0.0f; + + glm::dvec3 WorldRay = glm::inverse(ViewMatrix) * EyeCoordinates; + WorldRay = glm::normalize(WorldRay); + + return WorldRay; } \ No newline at end of file diff --git a/Core/FEGeometricTools.h b/Core/FEGeometricTools.h index 40c9650..4948e84 100644 --- a/Core/FEGeometricTools.h +++ b/Core/FEGeometricTools.h @@ -4,17 +4,11 @@ namespace FocalEngine { - class FEEntity; - class FEEntityInstanced; - class FETerrain; - class FEResourceManager; - class FEAABB { - friend FEEntity; - friend FEEntityInstanced; - friend FETerrain; - friend FEResourceManager; + friend class FEEntity; + friend class FEResourceManager; + friend class FEScene; public: FEAABB(); FEAABB(glm::vec3 Min, glm::vec3 Max); @@ -87,11 +81,28 @@ namespace FocalEngine float LongestAxisLength = 0.0f; }; - class FEGeometry + class FOCAL_ENGINE_API FEGeometry { public: SINGLETON_PUBLIC_PART(FEGeometry) + bool IsEpsilonEqual(const glm::dvec3& FirstVector, const glm::dvec3& SecondVector, double Epsilon = 1e-5); + bool IsEpsilonEqual(const glm::dquat& FirstQuaternion, const glm::dquat& SecondQuaternion, double Epsilon = 1e-5); + bool IsEpsilonEqual(const glm::dmat4& FirstMatrix, const glm::dmat4& SecondMatrix, double Epsilon = 1e-5); + + bool DecomposeMatrixToTranslationRotationScale(const glm::dmat4& Matrix, glm::dvec3& OutTranslation, glm::dquat& OutRotationQuaternion, glm::dvec3& OutScale); + + glm::vec3 CalculateNormal(glm::dvec3 FirstVertex, glm::dvec3 SecondVertex, glm::dvec3 ThirdVertex); + void CalculateNormals(const std::vector& Indices, const std::vector& Vertices, std::vector& NormalsToFill); + void CalculateNormals(const std::vector& Indices, const std::vector& Vertices, std::vector& NormalsToFill); + + glm::vec3 CalculateTangent(const glm::vec3 FirstVertex, const glm::vec3 SecondVertex, const glm::vec3 ThirdVertex, std::vector&& TextureCoordinates); + void CalculateTangents(const std::vector& Indices, const std::vector& Vertices, const std::vector& TextureCoordinates, const std::vector& Normals, std::vector& TangentsToFill); + void CalculateTangents(const std::vector& Indices, const std::vector& Vertices, const std::vector& TextureCoordinates, const std::vector& Normals, std::vector& TangentsToFill); + + glm::dvec3 CreateMouseRayToWorld(const double MouseScreenX, const double MouseScreenY, const glm::dmat4 ViewMatrix, const glm::dmat4 ProjectionMatrix, const glm::ivec2 ViewportPosition, const glm::ivec2 ViewportSize) const; + bool RaysIntersection(const glm::vec3& FirstRayOrigin, const glm::vec3& FirstRayDirection, const glm::vec3& SecondRayOrigin, const glm::vec3& SecondRayDirection, float& FirstRayParametricIntersection, float& SecondRayParametricIntersection) const; + bool IsRayIntersectingTriangle(glm::vec3 RayOrigin, glm::vec3 RayDirection, std::vector& TriangleVertices, float& Distance, glm::vec3* HitPoint = nullptr, float* U = nullptr, float* V = nullptr); bool IsRayIntersectingTriangle(glm::dvec3 RayOrigin, glm::dvec3 RayDirection, std::vector& TriangleVertices, double& Distance, glm::dvec3* HitPoint = nullptr, double* U = nullptr, double* V = nullptr); @@ -108,5 +119,10 @@ namespace FocalEngine SINGLETON_PRIVATE_PART(FEGeometry) }; -#define GEOMETRY FEGeometry::getInstance() +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetGeometry(); + #define GEOMETRY (*static_cast(GetGeometry())) +#else + #define GEOMETRY FEGeometry::GetInstance() +#endif } \ No newline at end of file diff --git a/Core/FEObject.cpp b/Core/FEObject.cpp index e69b827..beb326a 100644 --- a/Core/FEObject.cpp +++ b/Core/FEObject.cpp @@ -1,11 +1,16 @@ #include "FEObject.h" using namespace FocalEngine; -FEObjectManager* FEObjectManager::Instance = nullptr; +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetObjectManager() +{ + return FEObjectManager::GetInstancePointer(); +} +#endif FEObjectManager::FEObjectManager() { - ObjectsByType.resize(18); + ObjectsByType.resize(15); } FEObjectManager::~FEObjectManager() @@ -23,7 +28,7 @@ FEObject* FEObjectManager::GetFEObject(std::string ID) FEObject::FEObject(const FE_OBJECT_TYPE ObjectType, const std::string ObjectName) { ID = APPLICATION.GetUniqueHexID(); - + Type = ObjectType; Name = ObjectName; @@ -96,27 +101,25 @@ void FEObject::SetID(std::string NewValue) { if (ID == NewValue) { - LOG.Add("FEObject::setID newID is the same as current ID, redundant call", "FE_LOG_LOADING", FE_LOG_INFO); + LOG.Add("FEObject::SetID NewValue is the same as current ID, redundant call", "FE_LOG_GENERAL", FE_LOG_INFO); return; } if (OBJECT_MANAGER.AllObjects.find(ID) == OBJECT_MANAGER.AllObjects.end()) { + LOG.Add("FEObject::SetID current object is not found in AllObjects list!", "FE_LOG_GENERAL", FE_LOG_ERROR); assert(0); } if (OBJECT_MANAGER.AllObjects.find(NewValue) != OBJECT_MANAGER.AllObjects.end()) { + LOG.Add("FEObject::SetID New ID is already in use!", "FE_LOG_GENERAL", FE_LOG_ERROR); assert(0); } if (OBJECT_MANAGER.ObjectsByType[Type].find(ID) == OBJECT_MANAGER.ObjectsByType[Type].end()) { - assert(0); - } - - if (OBJECT_MANAGER.ObjectsByType[Type].find(NewValue) != OBJECT_MANAGER.ObjectsByType[Type].end()) - { + LOG.Add("FEObject::SetID current object is not found in ObjectsByType list!", "FE_LOG_GENERAL", FE_LOG_ERROR); assert(0); } @@ -138,13 +141,13 @@ void FEObject::SetIDOfUnTyped(const std::string NewValue) { if (Type != FE_NULL) { - LOG.Add("FEObject::setIDOfUnTyped type is FE_NULL", "FE_LOG_LOADING", FE_LOG_WARNING); + LOG.Add("FEObject::setIDOfUnTyped type is FE_NULL", "FE_LOG_GENERAL", FE_LOG_WARNING); return; } if (ID == NewValue) { - LOG.Add("FEObject::setIDOfUnTyped newID is the same as current ID, redundant call", "FE_LOG_LOADING", FE_LOG_INFO); + LOG.Add("FEObject::setIDOfUnTyped newID is the same as current ID, redundant call", "FE_LOG_GENERAL", FE_LOG_INFO); return; } @@ -178,4 +181,128 @@ void FEObject::SetIDOfUnTyped(const std::string NewValue) void FEObject::ProcessOnDeleteCallbacks(std::string DeletingFEObject) { +} + +std::string FEObject::GetTag() const +{ + return Tag; +} + +void FEObject::SetTag(std::string NewValue) +{ + Tag = NewValue; +} + +void FEObjectManager::SaveFEObjectPart(std::fstream& OpenedFile, FEObject* Object) +{ + if (Object == nullptr) + { + LOG.Add("FEObjectManager::SaveFEObjectPart Object is nullptr", "FE_LOG_GENERAL", FE_LOG_ERROR); + return; + } + + if (!OpenedFile.is_open()) + { + LOG.Add("FEObjectManager::SaveFEObjectPart file is not open", "FE_LOG_GENERAL", FE_LOG_ERROR); + return; + } + + int ObjectIDSize = static_cast(Object->GetObjectID().size() + 1); + OpenedFile.write((char*)&ObjectIDSize, sizeof(int)); + OpenedFile.write((char*)Object->GetObjectID().c_str(), sizeof(char) * ObjectIDSize); + + FE_OBJECT_TYPE ObjectType = Object->GetType(); + OpenedFile.write((char*)&ObjectType, sizeof(FE_OBJECT_TYPE)); + + int TagSize = static_cast(Object->GetTag().size() + 1); + OpenedFile.write((char*)&TagSize, sizeof(int)); + OpenedFile.write((char*)Object->GetTag().c_str(), sizeof(char) * TagSize); + + int NameSize = static_cast(Object->GetName().size() + 1); + OpenedFile.write((char*)&NameSize, sizeof(int)); + OpenedFile.write((char*)Object->GetName().c_str(), sizeof(char) * NameSize); +} + +FEObjectLoadedData FEObjectManager::LoadFEObjectPart(std::fstream& OpenedFile) +{ + FEObjectLoadedData Result; + + if (!OpenedFile.is_open()) + { + LOG.Add("FEObjectManager::LoadFEObjectPart file is not open", "FE_LOG_LOADING", FE_LOG_ERROR); + return Result; + } + + int ObjectIDSize = 0; + OpenedFile.read((char*)&ObjectIDSize, sizeof(int)); + char* ObjectID = new char[ObjectIDSize]; + OpenedFile.read(ObjectID, sizeof(char) * ObjectIDSize); + Result.ID = ObjectID; + delete[] ObjectID; + + FE_OBJECT_TYPE ObjectType; + OpenedFile.read((char*)&ObjectType, sizeof(FE_OBJECT_TYPE)); + Result.Type = ObjectType; + + int TagSize = 0; + OpenedFile.read((char*)&TagSize, sizeof(int)); + char* Tag = new char[TagSize]; + OpenedFile.read(Tag, sizeof(char) * TagSize); + Result.Tag = Tag; + delete[] Tag; + + int NameSize = 0; + OpenedFile.read((char*)&NameSize, sizeof(int)); + char* Name = new char[NameSize]; + OpenedFile.read(Name, sizeof(char) * NameSize); + Result.Name = Name; + delete[] Name; + + return Result; +} + +FEObjectLoadedData FEObjectManager::LoadFEObjectPart(char* FileData, int& CurrentShift) +{ + FEObjectLoadedData Result; + + if (FileData == nullptr) + { + LOG.Add("FEObjectManager::LoadFEObjectPart FileData is nullptr", "FE_LOG_LOADING", FE_LOG_ERROR); + return Result; + } + + int IDSize = *(int*)(&FileData[CurrentShift]); + CurrentShift += 4; + + Result.ID.clear(); + Result.ID.reserve(IDSize); + Result.ID.assign((char*)(&FileData[CurrentShift]), IDSize); + if (Result.ID[Result.ID.size() - 1] == '\0') + Result.ID.erase(Result.ID.size() - 1); + CurrentShift += IDSize; + + Result.Type = FE_OBJECT_TYPE (*(int*)(&FileData[CurrentShift])); + CurrentShift += 4; + + int TagSize = *(int*)(&FileData[CurrentShift]); + CurrentShift += 4; + + Result.Tag.clear(); + Result.Tag.reserve(TagSize); + Result.Tag.assign((char*)(&FileData[CurrentShift]), TagSize); + if (Result.Tag[Result.Tag.size() - 1] == '\0') + Result.Tag.erase(Result.Tag.size() - 1); + CurrentShift += TagSize; + + int NameSize = *(int*)(&FileData[CurrentShift]); + CurrentShift += 4; + + Result.Name.clear(); + Result.Name.reserve(NameSize); + Result.Name.assign((char*)(&FileData[CurrentShift]), NameSize); + if (Result.Name[Result.Name.size() - 1] == '\0') + Result.Name.erase(Result.Name.size() - 1); + CurrentShift += NameSize; + + return Result; } \ No newline at end of file diff --git a/Core/FEObject.h b/Core/FEObject.h index 71dd49e..d66e52f 100644 --- a/Core/FEObject.h +++ b/Core/FEObject.h @@ -16,41 +16,54 @@ namespace FocalEngine FE_MATERIAL = 4, FE_GAMEMODEL = 5, FE_ENTITY = 6, - FE_TERRAIN = 7, - FE_ENTITY_INSTANCED = 8, - FE_DIRECTIONAL_LIGHT = 9, - FE_POINT_LIGHT = 10, - FE_SPOT_LIGHT = 11, - FE_CAMERA = 12, - FE_FRAME_BUFFER = 13, - FE_POST_PROCESS = 14, - FE_TERRAIN_LAYER = 15, - FE_PREFAB = 16, - FE_VIRTUAL_UI_CONTEXT = 17 + FE_FRAME_BUFFER = 7, + FE_POST_PROCESS = 8, + FE_TERRAIN_LAYER = 9, + FE_PREFAB = 10, + FE_SCENE_GRAPH_NODE = 11, + FE_SCENE = 12, + FE_ASSET_PACKAGE = 13, + FE_NATIVE_SCRIPT_MODULE = 14 }; - class FEObject; - class FEngine; - class FERenderer; - class FEResourceManager; - class FEScene; + struct FEObjectLoadedData + { + std::string ID; + FE_OBJECT_TYPE Type = FE_NULL; + std::string Tag; + std::string Name; + }; - class FEObjectManager + class FOCAL_ENGINE_API FEObjectManager { - friend FEObject; - friend FEngine; - friend FERenderer; - friend FEResourceManager; - friend FEScene; + friend class FEObject; + friend class FEngine; + friend class FERenderer; + friend class FEResourceManager; + friend class FEScene; + friend class FESceneManager; public: SINGLETON_PUBLIC_PART(FEObjectManager) FEObject* GetFEObject(std::string ID); + // Takes open file stream and saves the object part of the file. + void SaveFEObjectPart(std::fstream& OpenedFile, FEObject* Object); + // Takes open file stream and loads the object part of the file, also returns bytes read. + FEObjectLoadedData LoadFEObjectPart(std::fstream& OpenedFile); + // Takes file data as char* and loads the object part, also returns bytes read. + FEObjectLoadedData LoadFEObjectPart(char* FileData, int& CurrentShift); private: SINGLETON_PRIVATE_PART(FEObjectManager) std::unordered_map AllObjects; std::vector> ObjectsByType; }; +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetObjectManager(); + #define OBJECT_MANAGER (*static_cast(GetObjectManager())) +#else + #define OBJECT_MANAGER FEObjectManager::GetInstance() +#endif + static std::string FEObjectTypeToString(const FE_OBJECT_TYPE Type) { switch (Type) @@ -83,41 +96,33 @@ namespace FocalEngine { return "FE_ENTITY"; } - case FocalEngine::FE_TERRAIN: - { - return "FE_TERRAIN"; - } - case FocalEngine::FE_ENTITY_INSTANCED: - { - return "FE_ENTITY_INSTANCED"; - } - case FocalEngine::FE_DIRECTIONAL_LIGHT: + case FocalEngine::FE_FRAME_BUFFER: { - return "FE_DIRECTIONAL_LIGHT"; + return "FE_FRAME_BUFFER"; } - case FocalEngine::FE_POINT_LIGHT: + case FocalEngine::FE_POST_PROCESS: { - return "FE_POINT_LIGHT"; + return "FE_POST_PROCESS"; } - case FocalEngine::FE_SPOT_LIGHT: + case FocalEngine::FE_PREFAB: { - return "FE_SPOT_LIGHT"; + return "FE_PREFAB"; } - case FocalEngine::FE_CAMERA: + case FocalEngine::FE_SCENE_GRAPH_NODE: { - return "FE_CAMERA"; + return "FE_SCENE_GRAPH_NODE"; } - case FocalEngine::FE_FRAME_BUFFER: + case FocalEngine::FE_SCENE: { - return "FE_FRAME_BUFFER"; + return "FE_SCENE"; } - case FocalEngine::FE_POST_PROCESS: + case FocalEngine::FE_ASSET_PACKAGE: { - return "FE_POST_PROCESS"; + return "FE_ASSET_PACKAGE"; } - case FocalEngine::FE_PREFAB: + case FocalEngine::FE_NATIVE_SCRIPT_MODULE: { - return "FE_PREFAB"; + return "FE_NATIVE_SCRIPT_MODULE"; } default: break; @@ -126,29 +131,24 @@ namespace FocalEngine return "FE_NULL"; } - class FEShader; - class FEMesh; - class FETexture; - class FEMaterial; - class FEGameModel; - class FEEntity; - class FETerrain; - class FEEntityInstanced; - class FEScene; - - class FEObject + class FOCAL_ENGINE_API FEObject { - friend FEngine; - friend FEShader; - friend FEMesh; - friend FETexture; - friend FEMaterial; - friend FEGameModel; - friend FEEntity; - friend FETerrain; - friend FEEntityInstanced; - friend FEResourceManager; - friend FEScene; + friend class FEObjectManager; + friend class FEngine; + friend class FERenderer; + friend class FEShader; + friend class FEMesh; + friend class FETexture; + friend class FEMaterial; + friend class FEGameModel; + friend class FEEntity; + friend class FEResourceManager; + friend class FENaiveSceneGraphNode; + friend class FEScene; + friend class FESceneManager; + friend class FETerrainSystem; + friend class FESkyDomeSystem; + friend class FENativeScriptSystem; public: FEObject(FE_OBJECT_TYPE ObjectType, std::string ObjectName); ~FEObject(); @@ -156,6 +156,8 @@ namespace FocalEngine std::string GetObjectID() const; FE_OBJECT_TYPE GetType() const; + std::string GetTag() const; + bool IsDirty() const; void SetDirtyFlag(bool NewValue); @@ -167,18 +169,19 @@ namespace FocalEngine private: std::string ID; FE_OBJECT_TYPE Type = FE_NULL; + std::string Tag = ""; bool bDirtyFlag = false; std::string Name; int NameHash = 0; void SetID(std::string NewValue); + void SetTag(std::string NewValue); void SetType(FE_OBJECT_TYPE NewValue); + protected: std::vector CallListOnDeleteFEObject; virtual void ProcessOnDeleteCallbacks(std::string DeletingFEObject); }; - - #define OBJECT_MANAGER FEObjectManager::getInstance() } #endif FEOBJECT_H \ No newline at end of file diff --git a/Core/FocalEngineAPI.h b/Core/FocalEngineAPI.h new file mode 100644 index 0000000..b330a92 --- /dev/null +++ b/Core/FocalEngineAPI.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef FOCAL_ENGINE_SHARED + #ifdef FOCAL_ENGINE_EXPORTS + #define FOCAL_ENGINE_API __declspec(dllexport) + #else + #define FOCAL_ENGINE_API __declspec(dllimport) + #endif +#else + #define FOCAL_ENGINE_API +#endif \ No newline at end of file diff --git a/CoreExtensions/FEFreeCamera.cpp b/CoreExtensions/FEFreeCamera.cpp deleted file mode 100644 index 38c0eea..0000000 --- a/CoreExtensions/FEFreeCamera.cpp +++ /dev/null @@ -1,200 +0,0 @@ -#include "FEFreeCamera.h" - -#include -using namespace FocalEngine; - -FEFreeCamera::FEFreeCamera(std::string Name) : FEBasicCamera (std::move(Name)) -{ - Type = 1; -} - -FEFreeCamera::~FEFreeCamera() = default; - -void FEFreeCamera::Move(const float DeltaTime) -{ - glm::vec4 Forward = { 0.0f, 0.0f, -(MovementSpeed * 2) * (DeltaTime / 1000), 0.0f }; - glm::vec4 Right = { (MovementSpeed * 2) * (DeltaTime / 1000), 0.0f, 0.0f, 0.0f }; - - Right = Right * ViewMatrix; - Forward = Forward * ViewMatrix; - - glm::normalize(Right); - glm::normalize(Forward); - - if (bLeftKeyPressed) - { - Position.x -= Right.x; - Position.y -= Right.y; - Position.z -= Right.z; - } - - if (bUpKeyPressed) - { - Position.x += Forward.x; - Position.y += Forward.y; - Position.z += Forward.z; - } - - if (bRightKeyPressed) - { - Position.x += Right.x; - Position.y += Right.y; - Position.z += Right.z; - } - - if (bDownKeyPressed) - { - Position.x -= Forward.x; - Position.y -= Forward.y; - Position.z -= Forward.z; - } - - UpdateViewMatrix(); - - if (ClientOnUpdateImpl) - ClientOnUpdateImpl(this); -} - -void FEFreeCamera::SetIsInputActive(const bool bIsActive) -{ - if (bIsActive) - { - SetCursorToCenter(); - } - else - { - bLeftKeyPressed = false; - bUpKeyPressed = false; - bDownKeyPressed = false; - bRightKeyPressed = false; - } - - FEBasicCamera::SetIsInputActive(bIsActive); -} - -void FEFreeCamera::Reset() -{ - CurrentMouseXAngle = 0; - CurrentMouseYAngle = 0; - - FEBasicCamera::Reset(); -} - -void FEFreeCamera::SetCursorToCenter() -{ - if (APPLICATION.GetMainWindow()->IsInFocus()) - { - LastMouseX = RenderTargetCenterX; - LastMouseY = RenderTargetCenterY; - - SetCursorPos(LastMouseX, LastMouseY); - - LastMouseX = LastMouseX - RenderTargetShiftX; - LastMouseY = LastMouseY - RenderTargetShiftY; - } -} - -void FEFreeCamera::MouseMoveInput(const double Xpos, const double Ypos) -{ - if (!bIsInputActive) - return; - - const int MouseX = static_cast(Xpos); - const int MouseY = static_cast(Ypos); - - if (LastMouseX == 0) LastMouseX = MouseX; - if (LastMouseY == 0) LastMouseY = MouseY; - - if (LastMouseX < MouseX || abs(LastMouseX - MouseX) > CorrectionToSensitivity) - { - CurrentMouseXAngle += (MouseX - LastMouseX) * 0.15f; - SetCursorToCenter(); - } - - if (LastMouseY < MouseY || abs(LastMouseY - MouseY) > CorrectionToSensitivity) - { - CurrentMouseYAngle += (MouseY - LastMouseY) * 0.15f; - SetCursorToCenter(); - } - - SetYaw(CurrentMouseXAngle); - if (CurrentMouseYAngle > 89.0f) - CurrentMouseYAngle = 89.0f; - if (CurrentMouseYAngle < -89.0f) - CurrentMouseYAngle = -89.0f; - SetPitch(CurrentMouseYAngle); -} - -void FEFreeCamera::KeyboardInput(const int Key, int Scancode, const int Action, int Mods) -{ - if (!bIsInputActive) - return; - - if (Key == GLFW_KEY_A && Action == GLFW_PRESS) - { - bLeftKeyPressed = true; - } - else if (Key == GLFW_KEY_A && Action == GLFW_RELEASE) - { - bLeftKeyPressed = false; - } - - if (Key == GLFW_KEY_W && Action == GLFW_PRESS) - { - bUpKeyPressed = true; - } - else if (Key == GLFW_KEY_W && Action == GLFW_RELEASE) - { - bUpKeyPressed = false; - } - - if (Key == GLFW_KEY_S && Action == GLFW_PRESS) - { - bDownKeyPressed = true; - } - else if (Key == GLFW_KEY_S && Action == GLFW_RELEASE) - { - bDownKeyPressed = false; - } - - if (Key == GLFW_KEY_D && Action == GLFW_PRESS) - { - bRightKeyPressed = true; - } - else if (Key == GLFW_KEY_D && Action == GLFW_RELEASE) - { - bRightKeyPressed = false; - } -} - -void FEFreeCamera::SetYaw(const float NewYaw) -{ - FEBasicCamera::SetYaw(NewYaw); - CurrentMouseXAngle = NewYaw; -} - -void FEFreeCamera::SetPitch(const float NewPitch) -{ - FEBasicCamera::SetPitch(NewPitch); - CurrentMouseYAngle = NewPitch; -} - -void FEFreeCamera::SetRenderTargetCenterX(const int NewRenderTargetCenterX) -{ - RenderTargetCenterX = NewRenderTargetCenterX; -} - -void FEFreeCamera::SetRenderTargetCenterY(const int NewRenderTargetCenterY) -{ - RenderTargetCenterY = NewRenderTargetCenterY; -} - -void FEFreeCamera::SetRenderTargetShiftX(const int NewRenderTargetShiftX) -{ - RenderTargetShiftX = NewRenderTargetShiftX; -} - -void FEFreeCamera::SetRenderTargetShiftY(const int NewRenderTargetShiftY) -{ - RenderTargetShiftY = NewRenderTargetShiftY; -} \ No newline at end of file diff --git a/CoreExtensions/FEFreeCamera.h b/CoreExtensions/FEFreeCamera.h deleted file mode 100644 index 473b196..0000000 --- a/CoreExtensions/FEFreeCamera.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "../SubSystems/FEBasicCamera.h" - -namespace FocalEngine -{ - class FEFreeCamera : public FEBasicCamera - { - public: - FEFreeCamera(std::string Name); - ~FEFreeCamera(); - - float CurrentMouseXAngle = 0; - float CurrentMouseYAngle = 0; - - void SetYaw(float NewYaw) override; - void SetPitch(float NewPitch) override; - - void SetIsInputActive(bool bIsActive) final; - - void KeyboardInput(int Key, int Scancode, int Action, int Mods) final; - void MouseMoveInput(double Xpos, double Ypos) final; - void Move(float DeltaTime = 0.0f) override; - - void Reset() override; - - void SetRenderTargetCenterX(int NewRenderTargetCenterX); - void SetRenderTargetCenterY(int NewRenderTargetCenterY); - - void SetRenderTargetShiftX(int NewRenderTargetShiftX); - void SetRenderTargetShiftY(int NewRenderTargetShiftY); - private: - int LastMouseX = 0; - int LastMouseY = 0; - - int RenderTargetCenterX = 0; - int RenderTargetCenterY = 0; - - int RenderTargetShiftX = 0; - int RenderTargetShiftY = 0; - - bool bLeftKeyPressed = false; - bool bUpKeyPressed = false; - bool bRightKeyPressed = false; - bool bDownKeyPressed = false; - - const int CorrectionToSensitivity = 3; - - void SetCursorToCenter(); - }; -} \ No newline at end of file diff --git a/CoreExtensions/FEModelViewCamera.cpp b/CoreExtensions/FEModelViewCamera.cpp deleted file mode 100644 index bddffe4..0000000 --- a/CoreExtensions/FEModelViewCamera.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include "FEModelViewCamera.h" -using namespace FocalEngine; - -FEModelViewCamera::FEModelViewCamera(const std::string Name) : FEBasicCamera(Name) -{ - Type = 2; -} - -FEModelViewCamera::~FEModelViewCamera() -{ - -} - -void FEModelViewCamera::Move(float DeltaTime) -{ - UpdateViewMatrix(); - - if (ClientOnUpdateImpl) - ClientOnUpdateImpl(this); -} - -void FEModelViewCamera::Reset() -{ - CurrentPolarAngle = 90.0; - CurrentAzimutAngle = 90.0; - DistanceToModel = 10.0; - - FEBasicCamera::Reset(); -} - -void FEModelViewCamera::MouseMoveInput(const double Xpos, const double Ypos) -{ - const int MouseX = static_cast(Xpos); - const int MouseY = static_cast(Ypos); - - if (!bIsInputActive) - { - LastMouseX = MouseX; - LastMouseY = MouseY; - return; - } - - if (LastMouseX != MouseX) - { - CurrentAzimutAngle += (MouseX - LastMouseX) * 0.1f; - } - - if (LastMouseY != MouseY) - { - SetPolarAngle(CurrentPolarAngle - (MouseY - LastMouseY) * 0.1f); - } - - LastMouseX = MouseX; - LastMouseY = MouseY; -} - -void FEModelViewCamera::KeyboardInput(int Key, int Scancode, int Action, int Mods) -{ - if (!bIsInputActive) - return; -} - -glm::dvec3 FEModelViewCamera::PolarToCartesian(double PolarAngle, double AzimutAngle, const double R) -{ - PolarAngle *= glm::pi() / 180.0; - AzimutAngle *= glm::pi() / 180.0; - - const double X = R * sin(PolarAngle) * cos(AzimutAngle); - const double Y = R * sin(PolarAngle) * sin(AzimutAngle); - const double Z = R * cos(PolarAngle); - - return glm::dvec3(X, Z, Y); -} - -void FEModelViewCamera::UpdateViewMatrix() -{ - ViewMatrix = glm::mat4(1.0f); - - Position = PolarToCartesian(CurrentPolarAngle, CurrentAzimutAngle, DistanceToModel); - ViewMatrix = glm::lookAt(Position, glm::vec3(0.0f), glm::vec3(0, 1, 0)); - - ViewMatrix = glm::translate(ViewMatrix, -TrackingObjectPosition); - Position += TrackingObjectPosition; -} - -double FEModelViewCamera::GetDistanceToModel() -{ - return DistanceToModel; -} - -void FEModelViewCamera::SetDistanceToModel(double NewValue) -{ - if (NewValue < 0.0) - NewValue = 0.1; - - DistanceToModel = NewValue; - UpdateViewMatrix(); -} - -void FEModelViewCamera::MouseScrollInput(const double Xoffset, const double Yoffset) -{ - if (!bIsInputActive) - return; - - SetDistanceToModel(DistanceToModel + Yoffset * 2.0); -} - -double FEModelViewCamera::GetPolarAngle() -{ - return CurrentPolarAngle; -} - -void FEModelViewCamera::SetPolarAngle(double NewValue) -{ - if (NewValue < 0.01) - NewValue = 0.011; - - if (NewValue > 179.98) - NewValue = 179.98; - - CurrentPolarAngle = NewValue; -} - -double FEModelViewCamera::GetAzimutAngle() -{ - return CurrentAzimutAngle; -} - -void FEModelViewCamera::SetAzimutAngle(const double NewValue) -{ - CurrentAzimutAngle = NewValue; -} - -glm::vec3 FEModelViewCamera::GetTrackingObjectPosition() -{ - return TrackingObjectPosition; -} - -void FEModelViewCamera::SetTrackingObjectPosition(const glm::vec3 NewValue) -{ - TrackingObjectPosition = NewValue; -} \ No newline at end of file diff --git a/CoreExtensions/FEModelViewCamera.h b/CoreExtensions/FEModelViewCamera.h deleted file mode 100644 index b5ced4c..0000000 --- a/CoreExtensions/FEModelViewCamera.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "../SubSystems/FEBasicCamera.h" - -namespace FocalEngine -{ - class FEModelViewCamera : public FEBasicCamera - { - public: - FEModelViewCamera(std::string Name); - ~FEModelViewCamera(); - - void KeyboardInput(int Key, int Scancode, int Action, int Mods) final; - void MouseMoveInput(double Xpos, double Ypos) final; - void MouseScrollInput(double Xoffset, double Yoffset) final; - void Move(float DeltaTime = 0.0f) override; - - void Reset() override; - void UpdateViewMatrix() override; - - double GetPolarAngle(); - void SetPolarAngle(double NewValue); - double GetAzimutAngle(); - void SetAzimutAngle(double NewValue); - - double GetDistanceToModel(); - void SetDistanceToModel(double NewValue); - - glm::vec3 GetTrackingObjectPosition(); - void SetTrackingObjectPosition(glm::vec3 NewValue); - private: - int LastMouseX = 0; - int LastMouseY = 0; - - double DistanceToModel = 10.0; - double CurrentPolarAngle = 90.0; - double CurrentAzimutAngle = 90.0; - - glm::vec3 TrackingObjectPosition = glm::vec3(0.0f); - - glm::dvec3 PolarToCartesian(double PolarAngle, double AzimutAngle, double R = 1.0); - }; -} \ No newline at end of file diff --git a/CoreExtensions/PostProcessEffects/FE_FXAA/FE_FXAA_FS.glsl b/CoreExtensions/PostProcessEffects/FE_FXAA/FE_FXAA_FS.glsl index 41a97d9..c5a54dc 100644 --- a/CoreExtensions/PostProcessEffects/FE_FXAA/FE_FXAA_FS.glsl +++ b/CoreExtensions/PostProcessEffects/FE_FXAA/FE_FXAA_FS.glsl @@ -7,14 +7,14 @@ in vec2 textureCoords; uniform float FXAASpanMax; uniform float FXAAReduceMin; uniform float FXAAReduceMul; -uniform vec2 FXAATextuxelSize; +uniform vec2 FXAATextureSize; @Texture@ inputTexture; out vec4 out_Color; void main(void) { - vec2 texCoordOffset = FXAATextuxelSize; + vec2 texCoordOffset = FXAATextureSize; vec3 luma = vec3(0.299, 0.587, 0.114); float lumaTL = dot(luma, texture2D(inputTexture, textureCoords.xy + (vec2(-1.0, -1.0) * texCoordOffset)).xyz); diff --git a/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS.glsl b/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS.glsl index 8ab323d..8962a58 100644 --- a/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS.glsl +++ b/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS.glsl @@ -35,10 +35,10 @@ uniform vec3 baseColor; struct FELight { - vec3 typeAndAngles; - vec3 position; - vec3 color; - vec3 direction; + vec4 typeAndAngles; + vec4 position; + vec4 color; + vec4 direction; mat4 lightSpace; }; @@ -400,11 +400,11 @@ void main(void) if (FElight[i].color.x == 0 && FElight[i].color.y == 0 && FElight[i].color.z == 0) continue; - if (FElight[i].typeAndAngles.x == 10) + if (FElight[i].typeAndAngles.x == 2) { outColor += vec4(pointLightColor(FElight[i], normal, FS_IN.worldPosition, viewDirection, baseColor), 1.0f); } - else if (FElight[i].typeAndAngles.x == 11) + else if (FElight[i].typeAndAngles.x == 3) { outColor += vec4(spotLightColor(FElight[i], normal, FS_IN.worldPosition, viewDirection, baseColor), 1.0f); } diff --git a/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS_DEFERRED.glsl b/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS_DEFERRED.glsl index 3f43a7a..2d6785e 100644 --- a/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS_DEFERRED.glsl +++ b/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS_DEFERRED.glsl @@ -36,10 +36,10 @@ uniform float SSAOActive; struct FELight { - vec3 typeAndAngles; - vec3 position; - vec3 color; - vec3 direction; + vec4 typeAndAngles; + vec4 position; + vec4 color; + vec4 direction; mat4 lightSpace; }; @@ -110,19 +110,16 @@ float getAO() float getRoughness() { - //return texture(textures[2], FS_IN.UV).g; return materialProperties.g; } float getMetalness() { - //return texture(textures[2], FS_IN.UV).b; return materialProperties.b; } float getDisplacement() { - //return texture(textures[2], FS_IN.UV).a; return materialProperties.a; } @@ -325,21 +322,28 @@ void main(void) if (FElight[i].color.x == 0 && FElight[i].color.y == 0 && FElight[i].color.z == 0) continue; - if (FElight[i].typeAndAngles.x == 1) + if (FElight[i].typeAndAngles.x == 2) { outColor += vec4(pointLightColor(FElight[i], normal, getWorldPosition(), viewDirection, baseColor), 1.0f); } - else if (FElight[i].typeAndAngles.x == 2) + else if (FElight[i].typeAndAngles.x == 3) { outColor += vec4(spotLightColor(FElight[i], normal, getWorldPosition(), viewDirection, baseColor), 1.0f); } } outColor += vec4(directionalLightColor(normal, getWorldPosition(), viewDirection, baseColor), 1.0f); - // shaderID tell us that it is terrain - if (texture(textures[4], FS_IN.UV).r == 1) + + // TODO: Make ShaderID more general, right now it pretty limited. + vec4 ShaderID = texture(textures[4], FS_IN.UV); + // If ShaderID is 1, then it is terrain + if (ShaderID.r == 1) outColor += vec4(texture(textures[4], FS_IN.UV).g, texture(textures[4], FS_IN.UV).b, texture(textures[4], FS_IN.UV).a , 1.0); + // If ShaderID is not 0 or 1, then it is clear color, pass it through without any changes + if ((ShaderID.r != 0 || ShaderID.g != 0 || ShaderID.b != 0) && (ShaderID.r != 1)) + outColor = getAlbedo(); + // test fog if (fogDensity > 0.0f && fogGradient > 0.0f) { diff --git a/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS_GBUFFER.glsl b/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS_GBUFFER.glsl index cb552e1..744e97c 100644 --- a/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS_GBUFFER.glsl +++ b/CoreExtensions/StandardMaterial/PBRMaterial/FE_PBR_FS_GBUFFER.glsl @@ -1,4 +1,7 @@ #define MAX_LIGHTS 10 + +@ProjectionMatrix@ + in VS_OUT { vec2 UV; @@ -15,6 +18,10 @@ layout (location = 2) out vec3 gNormal; layout (location = 3) out vec4 gAlbedo; layout (location = 4) out vec4 gMaterialProperties; layout (location = 5) out vec4 gShaderProperties; +layout (location = 6) out vec2 gMotionVectors; + +uniform mat4 FEPreviousFrameViewMatrix; +uniform vec2 ScreenSize; @MaterialTextures@ uniform float FENormalMapIntensity; @@ -215,6 +222,24 @@ void main(void) gMaterialProperties.b = getMetalness(); gMaterialProperties.a = getDisplacement(); gShaderProperties.r = 0; // 0 - shaderID + + // Motion vectors + vec2 MotionVectorsResult = vec2(0.0f, 0.0f); + vec4 PreviousClipPosition = FEProjectionMatrix * FEPreviousFrameViewMatrix * vec4(FS_IN.worldPosition.xyz, 1.0); + vec4 CurrentClipPosition = FEProjectionMatrix * FS_IN.viewPosition; + + if (CurrentClipPosition.w > 0 && PreviousClipPosition.w > 0) + { + CurrentClipPosition.xyz /= CurrentClipPosition.w; + PreviousClipPosition.xyz /= PreviousClipPosition.w; + + vec2 CurrentWindowPosition = CurrentClipPosition.xy * vec2(0.5); + vec2 PreviousWindowPosition = PreviousClipPosition.xy * vec2(0.5); + + MotionVectorsResult = (PreviousWindowPosition * ScreenSize) - (CurrentWindowPosition * ScreenSize); + } + + gMotionVectors = MotionVectorsResult; outColor = textureColor; -} +} \ No newline at end of file diff --git a/CoreExtensions/StandardMaterial/PhongMaterial/FE_Phong_FS.glsl b/CoreExtensions/StandardMaterial/PhongMaterial/FE_Phong_FS.glsl index bf4dcb6..70c72c7 100644 --- a/CoreExtensions/StandardMaterial/PhongMaterial/FE_Phong_FS.glsl +++ b/CoreExtensions/StandardMaterial/PhongMaterial/FE_Phong_FS.glsl @@ -15,10 +15,10 @@ uniform int debugFlag; struct FELight { - vec3 typeAndAngles; - vec3 position; - vec3 color; - vec3 direction; + vec4 typeAndAngles; + vec4 position; + vec4 color; + vec4 direction; mat4 lightSpace; }; @@ -155,11 +155,11 @@ void main(void) if (FElight[i].color.x == 0 && FElight[i].color.y == 0 && FElight[i].color.z == 0) continue; - if (FElight[i].typeAndAngles.x == 10) + if (FElight[i].typeAndAngles.x == 2) { out_Color += vec4(pointLightColor(FElight[i], normal, FS_IN.fragPosition, viewDirection, baseColor), 1.0f); } - else if (FElight[i].typeAndAngles.x == 11) + else if (FElight[i].typeAndAngles.x == 3) { out_Color += vec4(spotLightColor(FElight[i], normal, FS_IN.fragPosition, viewDirection, baseColor), 1.0f); } diff --git a/CoreExtensions/StandardMaterial/SkyDome/skyDome.h b/CoreExtensions/StandardMaterial/SkyDome/skyDome.h deleted file mode 100644 index 1132a8b..0000000 --- a/CoreExtensions/StandardMaterial/SkyDome/skyDome.h +++ /dev/null @@ -1,590 +0,0 @@ -#pragma once - -#include "../../../Renderer/FEShader.h" - -static const char* const FEPhongVS = R"( -#version 450 core -@In_Position@ -@In_UV@ -@In_Normal@ -@In_Tangent@ - -@WorldMatrix@ -@ViewMatrix@ -@ProjectionMatrix@ - -#define MAX_LIGHTS 10 -out VS_OUT -{ - vec2 UV; - vec3 fragPosition; - vec3 worldVertexPosition; - mat3 TBN; -} vs_out; - -void main(void) -{ - //vs_out.UV = FETexCoord; - - //vec3 T = normalize(vec3(FEWorldMatrix * vec4(FETangent, 0.0))); - //vec3 N = normalize(vec3(FEWorldMatrix * vec4(FENormal, 0.0))); - // re-orthogonalize T with respect to N - //T = normalize(T - dot(T, N) * N); - // then retrieve perpendicular vector B with the cross product of T and N - //vec3 B = cross(N, T); - //vs_out.TBN = mat3(T, B, N); - - //vs_out.fragPosition = vec3(FEViewMatrix * FEWorldMatrix * vec4(FEPosition, 1.0)); - vs_out.fragPosition = vec3(FEWorldMatrix * vec4(FEPosition, 1.0)); - //vs_out.worldVertexPosition = (FEWorldMatrix * vec4(FEPosition, 1.0)).xyz; - gl_Position = FEProjectionMatrix * FEViewMatrix * FEWorldMatrix * vec4(FEPosition, 1.0); -} -)"; - -static const char* const FEPhongFS = R"( -#version 450 core - -in VS_OUT -{ - vec2 UV; - vec3 fragPosition; - vec3 worldVertexPosition; - mat3 TBN; -} FS_IN; - -uniform float FEGamma; -uniform int debugFlag; - -precision highp float; - -#define PI 3.141592 -#define iSteps 16 -#define jSteps 8 - -vec2 rsi(vec3 r0, vec3 rd, float sr) { - // ray-sphere intersection that assumes - // the sphere is centered at the origin. - // No intersection when result.x > result.y - float a = dot(rd, rd); - float b = 2.0 * dot(rd, r0); - float c = dot(r0, r0) - (sr * sr); - float d = (b*b) - 4.0*a*c; - if (d < 0.0) return vec2(1e5,-1e5); - return vec2( - (-b - sqrt(d))/(2.0*a), - (-b + sqrt(d))/(2.0*a) - ); -} - -vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g) { - // Normalize the sun and view directions. - pSun = normalize(pSun); - r = normalize(r); - - // Calculate the step size of the primary ray. - vec2 p = rsi(r0, r, rAtmos); - if (p.x > p.y) return vec3(0,0,0); - p.y = min(p.y, rsi(r0, r, rPlanet).x); - float iStepSize = (p.y - p.x) / float(iSteps); - - // Initialize the primary ray time. - float iTime = 0.0; - - // Initialize accumulators for Rayleigh and Mie scattering. - vec3 totalRlh = vec3(0,0,0); - vec3 totalMie = vec3(0,0,0); - - // Initialize optical depth accumulators for the primary ray. - float iOdRlh = 0.0; - float iOdMie = 0.0; - - // Calculate the Rayleigh and Mie phases. - float mu = dot(r, pSun); - float mumu = mu * mu; - float gg = g * g; - float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu); - float pMie = 3.0 / (8.0 * PI) * ((1.0 - gg) * (mumu + 1.0)) / (pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + gg)); - - // Sample the primary ray. - for (int i = 0; i < iSteps; i++) { - - // Calculate the primary ray sample position. - vec3 iPos = r0 + r * (iTime + iStepSize * 0.5); - - // Calculate the height of the sample. - float iHeight = length(iPos) - rPlanet; - - // Calculate the optical depth of the Rayleigh and Mie scattering for this step. - float odStepRlh = exp(-iHeight / shRlh) * iStepSize; - float odStepMie = exp(-iHeight / shMie) * iStepSize; - - // Accumulate optical depth. - iOdRlh += odStepRlh; - iOdMie += odStepMie; - - // Calculate the step size of the secondary ray. - float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps); - - // Initialize the secondary ray time. - float jTime = 0.0; - - // Initialize optical depth accumulators for the secondary ray. - float jOdRlh = 0.0; - float jOdMie = 0.0; - - // Sample the secondary ray. - for (int j = 0; j < jSteps; j++) { - - // Calculate the secondary ray sample position. - vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5); - - // Calculate the height of the sample. - float jHeight = length(jPos) - rPlanet; - - // Accumulate the optical depth. - jOdRlh += exp(-jHeight / shRlh) * jStepSize; - jOdMie += exp(-jHeight / shMie) * jStepSize; - - // Increment the secondary ray time. - jTime += jStepSize; - } - - // Calculate attenuation. - vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh))); - - // Accumulate scattering. - totalRlh += odStepRlh * attn; - totalMie += odStepMie * attn; - - // Increment the primary ray time. - iTime += iStepSize; - - } - - // Calculate and return the final color. - return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie); -} - -void main(void) -{ - vec3 uSunPos = vec3(0.0, 0.3, -1.0); - vec3 color = atmosphere( - normalize(FS_IN.fragPosition), // normalized ray direction - vec3(0,6372e3,0), // ray origin - uSunPos, // position of the sun - 22.0, // intensity of the sun - 6371e3, // radius of the planet in meters - 6471e3, // radius of the atmosphere in meters - vec3(5.5e-6, 13.0e-6, 22.4e-6), // Rayleigh scattering coefficient - 21e-6, // Mie scattering coefficient - 8e3, // Rayleigh scale height - 1.2e3, // Mie scale height - 0.758 // Mie preferred scattering direction - ); - - // Apply exposure. - color = 1.0 - exp(-1.0 * color); - - gl_FragColor = vec4(color, 1.0f); -} - -)"; - -static const char* const FEPhongAOFS = R"( -#version 450 core - -#define MAX_LIGHTS 10 -in VS_OUT -{ - vec2 UV; - vec3 fragPosition; - vec3 worldVertexPosition; - mat3 TBN; -} FS_IN; - -@Texture@ baseColorTexture; -@Texture@ normalsTexture; -@Texture@ AOTexture; -@Texture@ roughtnessMap; -@CameraPosition@ -uniform float FEGamma; -uniform int debugFlag; - -struct FELight -{ - vec3 typeAndAngles; - vec3 position; - vec3 color; - vec3 direction; - mat4 lightSpace; - mat4 lightSpaceBig; -}; - -// is object receiving shadows. -@RECEVESHADOWS@ -// adds cascade shadow maps, 4 cascades. -@CSM@ - -layout (set = 0, binding = 0, std140) uniform lightInfo -{ - FELight FElight[MAX_LIGHTS]; -}; - -struct FEDirectionalLight -{ - vec3 position; - vec3 color; - vec3 direction; - mat4 CSM0; - mat4 CSM1; - mat4 CSM2; - mat4 CSM3; - int activeCascades; -}; - -#define CSM_MAX_CASCADE 3 - -layout (set = 0, binding = 1, std140) uniform directionalLightInfo -{ - FEDirectionalLight directionalLight; -}; - -vec3 directionalLightColor(vec3 normal, vec3 fragPosition, vec3 viewDir, vec3 baseColor); -vec3 pointLightColor(FELight light, vec3 normal, vec3 fragPosition, vec3 viewDir, vec3 baseColor); -vec3 spotLightColor(FELight light, vec3 normal, vec3 fragPosition, vec3 viewDir, vec3 baseColor); -float shadowCalculationCSM0(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir); -float shadowCalculationCSM1(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir); -float shadowCalculationCSM2(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir); -float shadowCalculationCSM3(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir); - -#define NUM_BLUR_TAPS 12 - -vec2 filterTaps[NUM_BLUR_TAPS] = vec2[] ( vec2(-0.326212, -0.405805), vec2(-0.840144, -0.07358), - vec2(-0.695914, 0.457137), vec2(-0.203345, 0.620716), - vec2( 0.96234, -0.194983), vec2( 0.473434, -0.480026), - vec2( 0.519456, 0.767022), vec2( 0.185461, -0.893124), - vec2( 0.507431, 0.064425), vec2( 0.89642, 0.412458), - vec2(-0.32194, -0.932615), vec2(-0.791559, -0.597705)); - -void main(void) -{ - // checking UV - if (debugFlag == 3) - { - gl_FragColor = vec4(FS_IN.UV.x, FS_IN.UV.y, 0.0, 1.0); - return; - } - // debug csm - else if (debugFlag == 4) - { - // CSM0 - vec4 vertexInLightSpace = directionalLight.CSM0 * vec4(FS_IN.worldVertexPosition, 1.0); - vec3 projCoords = vertexInLightSpace.xyz / vertexInLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - if (projCoords.x <= 0.9 && projCoords.y <= 0.9 && projCoords.x >= 0.1 && projCoords.y >= 0.1) - { - gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); - return; - } - - // CSM1 - if (directionalLight.activeCascades > 1) - { - vertexInLightSpace = directionalLight.CSM1 * vec4(FS_IN.worldVertexPosition, 1.0); - projCoords = vertexInLightSpace.xyz / vertexInLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - - if (projCoords.x <= 0.9 && projCoords.y <= 0.9 && projCoords.x >= 0.1 && projCoords.y >= 0.1) - { - gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); - return; - } - } - - // CSM2 - if (directionalLight.activeCascades > 2) - { - vertexInLightSpace = directionalLight.CSM2 * vec4(FS_IN.worldVertexPosition, 1.0); - projCoords = vertexInLightSpace.xyz / vertexInLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - - if (projCoords.x <= 0.9 && projCoords.y <= 0.9 && projCoords.x >= 0.1 && projCoords.y >= 0.1) - { - gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); - return; - } - } - - // CSM3 - if (directionalLight.activeCascades > 3) - { - vertexInLightSpace = directionalLight.CSM3 * vec4(FS_IN.worldVertexPosition, 1.0); - projCoords = vertexInLightSpace.xyz / vertexInLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - - if (projCoords.x <= 0.9 && projCoords.y <= 0.9 && projCoords.x >= 0.1 && projCoords.y >= 0.1) - { - gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); - return; - } - } - } - - vec4 textureColor = texture(baseColorTexture, FS_IN.UV); - if (textureColor.a < 0.05) - { - discard; - } - - vec3 baseColor = pow(textureColor.rgb, vec3(FEGamma)); - vec3 viewDirection = normalize(FECameraPosition - FS_IN.fragPosition); - vec3 ambientColor = (vec3(0.55f, 0.73f, 0.87f) * texture(AOTexture, FS_IN.UV).x) * 0.3f; - - vec3 normal = texture(normalsTexture, FS_IN.UV).rgb; - normal = normalize(normal * 2.0 - 1.0); - normal = normalize(FS_IN.TBN * normal); - - gl_FragColor = vec4(baseColor * ambientColor, 0.0f); - - for (int i = 0; i < MAX_LIGHTS; i++) - { - if (FElight[i].color.x == 0 && FElight[i].color.y == 0 && FElight[i].color.z == 0) - continue; - - if (FElight[i].typeAndAngles.x == 1) - { - gl_FragColor += vec4(pointLightColor(FElight[i], normal, FS_IN.fragPosition, viewDirection, baseColor), 1.0f); - } - else if (FElight[i].typeAndAngles.x == 2) - { - gl_FragColor += vec4(spotLightColor(FElight[i], normal, FS_IN.fragPosition, viewDirection, baseColor), 1.0f); - } - } - - gl_FragColor += vec4(directionalLightColor(normal, FS_IN.fragPosition, viewDirection, baseColor), 1.0f); -} - -float shadowCalculationCSM0(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) -{ - float shadow = 0.0; - - vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - float closestDepth = texture(CSM0, projCoords.xy).r; - float currentDepth = projCoords.z; - float bias = 0.001; - vec2 texelSize = 1.0 / textureSize(CSM0, 0); - float fScale = 0.02f; - - for(int i = 0; i < NUM_BLUR_TAPS; i++) - { - float pcfDepth = texture(CSM0, projCoords.xy + filterTaps[i] * texelSize).r; - shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; - } - shadow = shadow/NUM_BLUR_TAPS; - - return shadow; -} - -float shadowCalculationCSM1(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) -{ - float shadow = 0.0; - - vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - float closestDepth = texture(CSM1, projCoords.xy).r; - float currentDepth = projCoords.z; - float bias = 0.001; - vec2 texelSize = 1.0 / textureSize(CSM1, 0); - float fScale = 0.02f; - - for(int i = 0; i < NUM_BLUR_TAPS; i++) - { - float pcfDepth = texture(CSM1, projCoords.xy + filterTaps[i] * texelSize).r; - shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; - } - shadow = shadow/NUM_BLUR_TAPS; - - return shadow; -} - -float shadowCalculationCSM2(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) -{ - float shadow = 0.0; - - vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - float closestDepth = texture(CSM2, projCoords.xy).r; - float currentDepth = projCoords.z; - float bias = 0.001; - vec2 texelSize = 1.0 / textureSize(CSM2, 0); - float fScale = 0.02f; - - for(int i = 0; i < NUM_BLUR_TAPS; i++) - { - float pcfDepth = texture(CSM2, projCoords.xy + filterTaps[i] * texelSize).r; - shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; - } - shadow = shadow/NUM_BLUR_TAPS; - - return shadow; -} - -float shadowCalculationCSM3(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) -{ - float shadow = 0.0; - - vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - float closestDepth = texture(CSM3, projCoords.xy).r; - float currentDepth = projCoords.z; - float bias = 0.001; - vec2 texelSize = 1.0 / textureSize(CSM3, 0); - float fScale = 0.02f; - - for(int i = 0; i < NUM_BLUR_TAPS; i++) - { - float pcfDepth = texture(CSM3, projCoords.xy + filterTaps[i] * texelSize).r; - shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; - } - shadow = shadow/NUM_BLUR_TAPS; - - return shadow; -} - -vec3 directionalLightColor(vec3 normal, vec3 fragPosition, vec3 viewDir, vec3 baseColor) -{ - vec3 lightDirection = normalize(-directionalLight.direction.xyz); - - // diffuse part - float diffuseFactor = max(dot(normal, lightDirection), 0.0); - vec3 diffuseColor = diffuseFactor * directionalLight.color.xyz; - // specular part - vec3 reflectedDirection = reflect(-lightDirection, normal); - float specularFactor = pow(max(dot(viewDir, reflectedDirection), 0.0), 32); - - float specularStrength = 0.5; - specularStrength = (1.0 - texture(roughtnessMap, FS_IN.UV).r); - - specularStrength = max(specularStrength, 0.0); - - vec3 specular = specularStrength * specularFactor * directionalLight.color.xyz; - - if (FEReceiveShadows == 0) - return (baseColor * (diffuseColor + specular)); - - float shadow = 0.0; - - // first cascade - vec4 vertexInLightSpace = directionalLight.CSM0 * vec4(FS_IN.worldVertexPosition, 1.0); - vec3 projCoords = vertexInLightSpace.xyz / vertexInLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - if (projCoords.x <= 0.9 && projCoords.y <= 0.9 && projCoords.x >= 0.1 && projCoords.y >= 0.1) - { - shadow = shadowCalculationCSM0(vertexInLightSpace, normal, lightDirection); - return (baseColor * (diffuseColor + specular) * (1.0 - shadow)); - } - - // second cascade - if (directionalLight.activeCascades > 1) - { - vertexInLightSpace = directionalLight.CSM1 * vec4(FS_IN.worldVertexPosition, 1.0); - projCoords = vertexInLightSpace.xyz / vertexInLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - if (projCoords.x <= 0.9 && projCoords.y <= 0.9 && projCoords.x >= 0.1 && projCoords.y >= 0.1) - { - shadow = shadowCalculationCSM1(vertexInLightSpace, normal, lightDirection); - return (baseColor * (diffuseColor + specular) * (1.0 - shadow)); - } - } - - // third cascade - if (directionalLight.activeCascades > 2) - { - vertexInLightSpace = directionalLight.CSM2 * vec4(FS_IN.worldVertexPosition, 1.0); - projCoords = vertexInLightSpace.xyz / vertexInLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - if (projCoords.x <= 0.9 && projCoords.y <= 0.9 && projCoords.x >= 0.1 && projCoords.y >= 0.1) - { - shadow = shadowCalculationCSM2(vertexInLightSpace, normal, lightDirection); - return (baseColor * (diffuseColor + specular) * (1.0 - shadow)); - } - } - - // fourth cascade - if (directionalLight.activeCascades > 3) - { - vertexInLightSpace = directionalLight.CSM3 * vec4(FS_IN.worldVertexPosition, 1.0); - projCoords = vertexInLightSpace.xyz / vertexInLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; - if (projCoords.x <= 0.9 && projCoords.y <= 0.9 && projCoords.x >= 0.1 && projCoords.y >= 0.1) - { - shadow = shadowCalculationCSM3(vertexInLightSpace, normal, lightDirection); - return (baseColor * (diffuseColor + specular) * (1.0 - shadow)); - } - } - - return (baseColor * (diffuseColor + specular) * (1.0 - shadow)); -} - -vec3 pointLightColor(FELight light, vec3 normal, vec3 fragPosition, vec3 viewDir, vec3 baseColor) -{ - float distance = length(light.position.xyz - fragPosition); - float attenuation = 1.0 / (1.0 + 0.09 * distance + 0.032 * (distance * distance)); - vec3 lightDirection = normalize(light.position.xyz - fragPosition); - - // diffuse part - float diffuseFactor = max(dot(normal, lightDirection), 0.0); - vec3 diffuseColor = diffuseFactor * light.color.xyz; - // specular part - vec3 reflectedDirection = reflect(-lightDirection, normal); - float specularFactor = pow(max(dot(viewDir, reflectedDirection), 0.0), 32); - float specularStrength = 0.5; - specularStrength = max(specularStrength, 0.0); - - vec3 specular = specularStrength * specularFactor * light.color.xyz; - - return (baseColor * (diffuseColor * attenuation + specular * attenuation)); -} - -vec3 spotLightColor(FELight light, vec3 normal, vec3 fragPosition, vec3 viewDir, vec3 baseColor) -{ - vec3 lightDirection = normalize(light.position.xyz - fragPosition); - float theta = dot(lightDirection, normalize(-light.direction.xyz)); - if(theta > light.typeAndAngles.z) - { - float epsilon = light.typeAndAngles.y - light.typeAndAngles.z; - float intensity = clamp((theta - light.typeAndAngles.z) / epsilon, 0.0, 1.0); - float distance = length(light.position.xyz - fragPosition); - float attenuation = 1.0 / (1.0 + 0.09 * distance + 0.032 * (distance * distance)); - - // diffuse part - float diffuseFactor = max(dot(normal, lightDirection), 0.0); - vec3 diffuseColor = diffuseFactor * light.color.xyz; - // specular part - vec3 reflectedDirection = reflect(-lightDirection, normal); - float specularFactor = pow(max(dot(viewDir, reflectedDirection), 0.0), 32); - float specularStrength = 0.5; - specularStrength = max(specularStrength, 0.0); - - vec3 specular = specularStrength * specularFactor * light.color.xyz; - - return (baseColor * (diffuseColor * attenuation * intensity + specular * attenuation * intensity)); - } - - return vec3(0.0, 0.0, 0.0); -} - -)"; - -namespace FocalEngine -{ - class FEPhongShader : public FEShader - { - public: - FEPhongShader(); - ~FEPhongShader(); - - private: - }; -} \ No newline at end of file diff --git a/CoreExtensions/StandardMaterial/TerrainMaterial/FE_Terrain_FS_DEFERRED.glsl b/CoreExtensions/StandardMaterial/TerrainMaterial/FE_Terrain_FS_DEFERRED.glsl index f3c7077..6eb2439 100644 --- a/CoreExtensions/StandardMaterial/TerrainMaterial/FE_Terrain_FS_DEFERRED.glsl +++ b/CoreExtensions/StandardMaterial/TerrainMaterial/FE_Terrain_FS_DEFERRED.glsl @@ -42,10 +42,10 @@ uniform float fogGradient; struct FELight { - vec3 typeAndAngles; - vec3 position; - vec3 color; - vec3 direction; + vec4 typeAndAngles; + vec4 position; + vec4 color; + vec4 direction; mat4 lightSpace; }; @@ -422,11 +422,11 @@ void main(void) if (FElight[i].color.x == 0 && FElight[i].color.y == 0 && FElight[i].color.z == 0) continue; - if (FElight[i].typeAndAngles.x == 1) + if (FElight[i].typeAndAngles.x == 2) { outColor += vec4(pointLightColor(FElight[i], normal, FS_IN.worldPosition, viewDirection, baseColor), 1.0f); } - else if (FElight[i].typeAndAngles.x == 2) + else if (FElight[i].typeAndAngles.x == 3) { outColor += vec4(spotLightColor(FElight[i], normal, FS_IN.worldPosition, viewDirection, baseColor), 1.0f); } diff --git a/CoreExtensions/StandardMaterial/TerrainMaterial/FE_Terrain_FS_GBUFFER.glsl b/CoreExtensions/StandardMaterial/TerrainMaterial/FE_Terrain_FS_GBUFFER.glsl index e9e7cb8..deca01e 100644 --- a/CoreExtensions/StandardMaterial/TerrainMaterial/FE_Terrain_FS_GBUFFER.glsl +++ b/CoreExtensions/StandardMaterial/TerrainMaterial/FE_Terrain_FS_GBUFFER.glsl @@ -48,6 +48,11 @@ layout (location = 2) out vec3 gNormal; layout (location = 3) out vec4 gAlbedo; layout (location = 4) out vec4 gMaterialProperties; layout (location = 5) out vec4 gShaderProperties; +layout (location = 6) out vec2 gMotionVectors; + +@ProjectionMatrix@ +uniform mat4 FEPreviousFrameViewMatrix; +uniform vec2 ScreenSize; @Texture@ projectedMap; @@ -308,5 +313,23 @@ void main(void) gShaderProperties.b = texture(projectedMap, FS_IN.UV / tileMult).g; gShaderProperties.a = texture(projectedMap, FS_IN.UV / tileMult).b; + // Motion vectors + vec2 MotionVectorsResult = vec2(0.0f, 0.0f); + vec4 PreviousClipPosition = FEProjectionMatrix * FEPreviousFrameViewMatrix * vec4(FS_IN.worldPosition.xyz, 1.0); + vec4 CurrentClipPosition = FEProjectionMatrix * FS_IN.viewPosition; + + if (CurrentClipPosition.w > 0 && PreviousClipPosition.w > 0) + { + CurrentClipPosition.xyz /= CurrentClipPosition.w; + PreviousClipPosition.xyz /= PreviousClipPosition.w; + + vec2 CurrentWindowPosition = CurrentClipPosition.xy * vec2(0.5); + vec2 PreviousWindowPosition = PreviousClipPosition.xy * vec2(0.5); + + MotionVectorsResult = (PreviousWindowPosition * ScreenSize) - (CurrentWindowPosition * ScreenSize); + } + + gMotionVectors = MotionVectorsResult; + outColor = finalColor; } diff --git a/FEngine.cpp b/FEngine.cpp index 6e4e73a..5a54b33 100644 --- a/FEngine.cpp +++ b/FEngine.cpp @@ -1,10 +1,12 @@ #include "FEngine.h" using namespace FocalEngine; -FEngine* FEngine::Instance = nullptr; -FE_RENDER_TARGET_MODE FEngine::RenderTargetMode = FE_GLFW_MODE; -int FEngine::RenderTargetXShift = 0; -int FEngine::RenderTargetYShift = 0; +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetEngine() +{ + return FEngine::GetInstancePointer(); +} +#endif FEngine::FEngine() { @@ -14,11 +16,45 @@ FEngine::~FEngine() { } +#include "ResourceManager/Timestamp.h" +std::string FEngine::GetEngineBuildVersion() +{ + return ENGINE_BUILD_TIMESTAMP; +} + bool FEngine::IsNotTerminated() { return APPLICATION.IsNotTerminated(); } +void FEngine::InternalUpdate() +{ + CurrentDeltaTime = CPUTime + GPUTime; + + ViewportCheckForModification(); + + INSTANCED_RENDERING_SYSTEM.Update(); + SCENE_MANAGER.Update(); + CAMERA_SYSTEM.Update(CurrentDeltaTime); + VIRTUAL_UI_SYSTEM.Update(); + + NATIVE_SCRIPT_SYSTEM.Update(CurrentDeltaTime); + + for (size_t i = 0; i < OnAfterUpdateCallbacks.size(); i++) + { + if (OnAfterUpdateCallbacks[i] == nullptr) + continue; + + OnAfterUpdateCallbacks[i](); + } + + // Instead of updating TRANSFORM_SYSTEM in the beginning of the frame, we update it here. + // To ensure that all the other systems are updated before the TRANSFORM_SYSTEM will kick in. + TRANSFORM_SYSTEM.Update(); + + INPUT.Update(); +} + void FEngine::BeginFrame(const bool InternalCall) { if (!APPLICATION.IsNotTerminated()) @@ -36,8 +72,8 @@ void FEngine::BeginFrame(const bool InternalCall) APPLICATION.GetMainWindow()->BeginFrame(); #ifdef FE_DEBUG_ENABLED - std::vector ShaderList = RESOURCE_MANAGER.GetShadersList(); - const std::vector TempList = RESOURCE_MANAGER.GetStandardShadersList(); + std::vector ShaderList = RESOURCE_MANAGER.GetShaderIDList(); + const std::vector TempList = RESOURCE_MANAGER.GetEnginePrivateShaderIDList(); for (size_t i = 0; i < TempList.size(); i++) { ShaderList.push_back(TempList[i]); @@ -51,22 +87,22 @@ void FEngine::BeginFrame(const bool InternalCall) } } #endif + + InternalUpdate(); } void FEngine::Render(const bool InternalCall) { - RENDERER.EngineMainCamera = ENGINE.CurrentCamera; - RENDERER.MouseRay = ENGINE.ConstructMouseRay(); - ENGINE.CurrentCamera->Move(static_cast(CPUTime + GPUTime)); - RENDERER.Render(CurrentCamera); + std::vector ActiveScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active | FESceneFlag::Renderable); + for (size_t i = 0; i < ActiveScenes.size(); i++) + { + RENDERER.Render(ActiveScenes[i]); + } if (bVRActive) { OpenXR_MANAGER.Update(); - glViewport(static_cast(0.0), - static_cast(0.0), - static_cast(ENGINE.GetRenderTargetWidth()), - static_cast(ENGINE.GetRenderTargetHeight())); + RENDERER.SetGLViewport(0, 0, ENGINE.GetDefaultViewport()->GetWidth(), ENGINE.GetDefaultViewport()->GetHeight()); } APPLICATION.GetMainWindow()->Render(); @@ -76,37 +112,32 @@ void FEngine::Render(const bool InternalCall) void FEngine::EndFrame(const bool InternalCall) { + INPUT.EndFrame(); + if (!InternalCall) TIME.BeginTimeStamp(); APPLICATION.GetMainWindow()->EndFrame(); APPLICATION.EndFrame(); if (!InternalCall) GPUTime = TIME.EndTimeStamp(); + + // FIXME: Since AssetPackage doesn't extract assets directly to memory, we need to delete the directory after the frame completes. + if (RESOURCE_MANAGER.PrivateEngineAssetPackage != nullptr) + { + FILE_SYSTEM.DeleteDirectory(FILE_SYSTEM.GetCurrentWorkingPath() + "/SubSystems"); + delete RESOURCE_MANAGER.PrivateEngineAssetPackage; + RESOURCE_MANAGER.PrivateEngineAssetPackage = nullptr; + } + + CurentFrameIndex++; } void FEngine::InitWindow(const int Width, const int Height, std::string WindowTitle) { - WindowW = Width; - WindowH = Height; - this->WindowTitle = WindowTitle; - - APPLICATION.AddWindow(Width, Height, WindowTitle); + FEWindow* NewWindow = APPLICATION.AddWindow(Width, Height, WindowTitle); + // Early initialization of INPUT system. + INPUT; APPLICATION.GetMainWindow()->AddOnResizeCallback(&FEngine::WindowResizeCallback); - APPLICATION.GetMainWindow()->AddOnMouseButtonCallback(&FEngine::MouseButtonCallback); - APPLICATION.GetMainWindow()->AddOnMouseMoveCallback(&FEngine::MouseMoveCallback); - APPLICATION.GetMainWindow()->AddOnKeyCallback(&FEngine::KeyButtonCallback); APPLICATION.GetMainWindow()->AddOnDropCallback(&FEngine::DropCallback); - APPLICATION.GetMainWindow()->AddOnScrollCallback(&FEngine::MouseScrollCallback); - - SetClearColor(DefaultGammaCorrectedClearColor); - - CurrentCamera = new FEFreeCamera("mainCamera"); - int FinalWidth, FinalHeight; - APPLICATION.GetMainWindow()->GetSize(&FinalWidth, &FinalHeight); - - WindowW = FinalWidth; - WindowH = FinalHeight; - RenderTargetW = FinalWidth; - RenderTargetH = FinalHeight; - CurrentCamera->SetAspectRatio(static_cast(GetRenderTargetWidth()) / static_cast(GetRenderTargetHeight())); + CreateViewport(NewWindow); FE_GL_ERROR(glEnable(GL_DEPTH_TEST)); @@ -114,189 +145,17 @@ void FEngine::InitWindow(const int Width, const int Height, std::string WindowTi FE_GL_ERROR(glPatchParameteri(GL_PATCH_VERTICES, 4)); RENDERER.Init(); - RENDERER.InstancedLineShader = RESOURCE_MANAGER.CreateShader("instancedLine", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//InstancedLineMaterial//FE_InstancedLine_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//InstancedLineMaterial//FE_InstancedLine_FS.glsl").c_str()).c_str()); - RESOURCE_MANAGER.Shaders.erase(RENDERER.InstancedLineShader->GetObjectID()); - RENDERER.InstancedLineShader->SetID("7E0826291010377D564F6115"/*"instancedLine"*/); - RESOURCE_MANAGER.Shaders[RENDERER.InstancedLineShader->GetObjectID()] = RENDERER.InstancedLineShader; - - FEShader* FEScreenQuadShader = RESOURCE_MANAGER.CreateShader("FEScreenQuadShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_ScreenQuad_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_ScreenQuad_FS.glsl").c_str()).c_str()); - RESOURCE_MANAGER.Shaders.erase(FEScreenQuadShader->GetObjectID()); - FEScreenQuadShader->SetID("7933272551311F3A1A5B2363"/*"FEScreenQuadShader"*/); - RESOURCE_MANAGER.Shaders[FEScreenQuadShader->GetObjectID()] = FEScreenQuadShader; - RESOURCE_MANAGER.MakeShaderStandard(FEScreenQuadShader); - - RENDERER.StandardFBInit(GetRenderTargetWidth(), GetRenderTargetHeight()); - - // ************************************ Bloom ************************************ - FEPostProcess* BloomEffect = ENGINE.CreatePostProcess("bloom", static_cast(GetRenderTargetWidth() / 4.0f), static_cast(GetRenderTargetHeight() / 4.0f)); - BloomEffect->SetID("451C48791871283D372C5938"/*"bloom"*/); - - FEShader* BloomThresholdShader = - RESOURCE_MANAGER.CreateShader("FEBloomThreshold", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_Bloom_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_BloomThreshold_FS.glsl").c_str()).c_str()); - RESOURCE_MANAGER.Shaders.erase(BloomThresholdShader->GetObjectID()); - BloomThresholdShader->SetID("0C19574118676C2E5645200E"/*"FEBloomThreshold"*/); - RESOURCE_MANAGER.Shaders[BloomThresholdShader->GetObjectID()] = BloomThresholdShader; - - BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_SCENE_HDR_COLOR, BloomThresholdShader)); - BloomEffect->Stages[0]->Shader->UpdateParameterData("thresholdBrightness", 1.0f); - - FEShader* BloomBlurShader = - RESOURCE_MANAGER.CreateShader("FEBloomBlur", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_Bloom_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_BloomBlur_FS.glsl").c_str()).c_str()); - RESOURCE_MANAGER.Shaders.erase(BloomBlurShader->GetObjectID()); - BloomBlurShader->SetID("7F3E4F5C130B537F0846274F"/*"FEBloomBlur"*/); - RESOURCE_MANAGER.Shaders[BloomBlurShader->GetObjectID()] = BloomBlurShader; - - BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, BloomBlurShader)); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(0.0f, 1.0f), "FEBlurDirection")); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(5.0f, "BloomSize")); - - BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, BloomBlurShader)); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(1.0f, 0.0f), "FEBlurDirection")); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(5.0f, "BloomSize")); - - BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, BloomBlurShader)); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(0.0f, 1.0f), "FEBlurDirection")); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(1.0f, "BloomSize")); - - BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, BloomBlurShader)); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(1.0f, 0.0f), "FEBlurDirection")); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(1.0f, "BloomSize")); - - FEShader* BloomCompositionShader = - RESOURCE_MANAGER.CreateShader("FEBloomComposition", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_Bloom_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_BloomComposition_FS.glsl").c_str()).c_str()); - RESOURCE_MANAGER.Shaders.erase(BloomCompositionShader->GetObjectID()); - BloomCompositionShader->SetID("1833272551376C2E5645200E"/*"FEBloomComposition"*/); - RESOURCE_MANAGER.Shaders[BloomCompositionShader->GetObjectID()] = BloomCompositionShader; - - BloomEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, FE_POST_PROCESS_SCENE_HDR_COLOR}, BloomCompositionShader)); - - RENDERER.AddPostProcess(BloomEffect); - // ************************************ Bloom END ************************************ - - // ************************************ gammaHDR ************************************ - FEPostProcess* GammaHDR = ENGINE.CreatePostProcess("GammaAndHDR", GetRenderTargetWidth(), GetRenderTargetHeight()); - GammaHDR->SetID("2374462A7B0E78141B5F5D79"/*"GammaAndHDR"*/); - - FEShader* GammaHDRShader = - RESOURCE_MANAGER.CreateShader("FEGammaAndHDRShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_GammaAndHDRCorrection//FE_Gamma_and_HDR_Correction_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_GammaAndHDRCorrection//FE_Gamma_and_HDR_Correction_FS.glsl").c_str()).c_str()); - RESOURCE_MANAGER.Shaders.erase(GammaHDRShader->GetObjectID()); - GammaHDRShader->SetID("3417497A5E0C0C2A07456E44"/*"FEGammaAndHDRShader"*/); - RESOURCE_MANAGER.Shaders[GammaHDRShader->GetObjectID()] = GammaHDRShader; - - GammaHDR->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, GammaHDRShader)); - RENDERER.AddPostProcess(GammaHDR); - // ************************************ gammaHDR END ************************************ - - // ************************************ FXAA ************************************ - FEPostProcess* FEFXAAEffect = ENGINE.CreatePostProcess("FE_FXAA", GetRenderTargetWidth(), GetRenderTargetHeight()); - FEFXAAEffect->SetID("0A3F10643F06525D70016070"/*"FE_FXAA"*/); - - FEShader* FEFXAAShader = - RESOURCE_MANAGER.CreateShader("FEFXAAShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_FXAA//FE_FXAA_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_FXAA//FE_FXAA_FS.glsl").c_str()).c_str()); - RESOURCE_MANAGER.Shaders.erase(FEFXAAShader->GetObjectID()); - FEFXAAShader->SetID("1E69744A10604C2A1221426B"/*"FEFXAAShader"*/); - RESOURCE_MANAGER.Shaders[FEFXAAShader->GetObjectID()] = FEFXAAShader; - - FEFXAAEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, FEFXAAShader)); - FEFXAAEffect->Stages.back()->Shader->UpdateParameterData("FXAASpanMax", 8.0f); - FEFXAAEffect->Stages.back()->Shader->UpdateParameterData("FXAAReduceMin", 1.0f / 128.0f); - FEFXAAEffect->Stages.back()->Shader->UpdateParameterData("FXAAReduceMul", 0.4f); - FEFXAAEffect->Stages.back()->Shader->UpdateParameterData("FXAATextuxelSize", glm::vec2(1.0f / GetRenderTargetWidth(), 1.0f / GetRenderTargetHeight())); - RENDERER.AddPostProcess(FEFXAAEffect); - - //#fix for now after gamma correction I assume that texture output should be GL_RGB but in future it should be changeable. - RENDERER.PostProcessEffects.back()->ReplaceOutTexture(0, RESOURCE_MANAGER.CreateTexture(GL_RGB, GL_RGB, GetRenderTargetWidth(), GetRenderTargetHeight())); - - // ************************************ FXAA END ************************************ - - // ************************************ DOF ************************************ - FEPostProcess* DOFEffect = ENGINE.CreatePostProcess("DOF"); - DOFEffect->SetID("217C4E80482B6C650D7B492F"/*"DOF"*/); - - FEShader* DOFShader = RESOURCE_MANAGER.CreateShader("DOF", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_DOF//FE_DOF_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_DOF//FE_DOF_FS.glsl").c_str()).c_str()); - RESOURCE_MANAGER.Shaders.erase(DOFShader->GetObjectID()); - DOFShader->SetID("7800253C244442155D0F3C7B"/*"DOF"*/); - RESOURCE_MANAGER.Shaders[DOFShader->GetObjectID()] = DOFShader; - - DOFEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, FE_POST_PROCESS_SCENE_DEPTH}, DOFShader)); - DOFEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(0.0f, 1.0f), "FEBlurDirection")); - DOFEffect->Stages.back()->Shader->UpdateParameterData("blurSize", 2.0f); - DOFEffect->Stages.back()->Shader->UpdateParameterData("depthThreshold", 0.0f); - DOFEffect->Stages.back()->Shader->UpdateParameterData("depthThresholdFar", 9000.0f); - DOFEffect->Stages.back()->Shader->UpdateParameterData("zNear", 0.1f); - DOFEffect->Stages.back()->Shader->UpdateParameterData("zFar", 5000.0f); - DOFEffect->Stages.back()->Shader->UpdateParameterData("intMult", 100.0f); - DOFEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, FE_POST_PROCESS_SCENE_DEPTH}, DOFShader)); - DOFEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(1.0f, 0.0f), "FEBlurDirection")); - DOFEffect->Stages.back()->Shader->UpdateParameterData("blurSize", 2.0f); - DOFEffect->Stages.back()->Shader->UpdateParameterData("depthThreshold", 0.0f); - DOFEffect->Stages.back()->Shader->UpdateParameterData("depthThresholdFar", 9000.0f); - DOFEffect->Stages.back()->Shader->UpdateParameterData("zNear", 0.1f); - DOFEffect->Stages.back()->Shader->UpdateParameterData("zFar", 5000.0f); - DOFEffect->Stages.back()->Shader->UpdateParameterData("intMult", 100.0f); - RENDERER.AddPostProcess(DOFEffect); - // ************************************ DOF END ************************************ - - // ************************************ chromaticAberrationEffect ************************************ - FEPostProcess* ChromaticAberrationEffect = ENGINE.CreatePostProcess("chromaticAberration"); - ChromaticAberrationEffect->SetID("506D804162647749060C3E68"/*"chromaticAberration"*/); - - FEShader* ChromaticAberrationShader = RESOURCE_MANAGER.CreateShader("chromaticAberrationShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_ChromaticAberration//FE_ChromaticAberration_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_ChromaticAberration//FE_ChromaticAberration_FS.glsl").c_str()).c_str()); - RESOURCE_MANAGER.Shaders.erase(ChromaticAberrationShader->GetObjectID()); - ChromaticAberrationShader->SetID("9A41665B5E2B05321A332D09"/*"chromaticAberrationShader"*/); - RESOURCE_MANAGER.Shaders[ChromaticAberrationShader->GetObjectID()] = ChromaticAberrationShader; - - ChromaticAberrationEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0 }, ChromaticAberrationShader)); - ChromaticAberrationEffect->Stages.back()->Shader->UpdateParameterData("intensity", 1.0f); - RENDERER.AddPostProcess(ChromaticAberrationEffect); - //#fix for now after gamma correction I assume that texture output should be GL_RGB but in future it should be changeable. - RENDERER.PostProcessEffects.back()->ReplaceOutTexture(0, RESOURCE_MANAGER.CreateTexture(GL_RGB, GL_RGB, GetRenderTargetWidth(), GetRenderTargetHeight())); - // ************************************ chromaticAberrationEffect END ************************************ - - // ************************************ SSAO ************************************ - FEShader* FESSAOShader = RESOURCE_MANAGER.CreateShader("FESSAOShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_SSAO//FE_SSAO_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_SSAO//FE_SSAO_FS.glsl").c_str()).c_str()); - - RESOURCE_MANAGER.Shaders.erase(FESSAOShader->GetObjectID()); - FESSAOShader->SetID("1037115B676E383E36345079"/*"FESSAOShader"*/); - RESOURCE_MANAGER.Shaders[FESSAOShader->GetObjectID()] = FESSAOShader; - - RESOURCE_MANAGER.MakeShaderStandard(FESSAOShader); - - FEShader* FESSAOBlurShader = RESOURCE_MANAGER.CreateShader("FESSAOBlurShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_ScreenQuad_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_SSAO//FE_SSAO_Blur_FS.glsl").c_str()).c_str()); - RESOURCE_MANAGER.Shaders.erase(FESSAOBlurShader->GetObjectID()); - FESSAOBlurShader->SetID("0B5770660B6970800D776542"/*"FESSAOBlurShader"*/); - RESOURCE_MANAGER.Shaders[FESSAOBlurShader->GetObjectID()] = FESSAOBlurShader; - - RESOURCE_MANAGER.MakeShaderStandard(FESSAOBlurShader); - // ************************************ SSAO END ************************************ - - RENDERER.ShadowMapMaterial = RESOURCE_MANAGER.CreateMaterial("shadowMapMaterial"); - RENDERER.ShadowMapMaterial->Shader = RESOURCE_MANAGER.CreateShader("FEShadowMapShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//ShadowMapMaterial//FE_ShadowMap_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//ShadowMapMaterial//FE_ShadowMap_FS.glsl").c_str()).c_str()); - RENDERER.ShadowMapMaterial->Shader->SetID("7C41565B2E2B05321A182D89"/*"FEShadowMapShader"*/); - - RESOURCE_MANAGER.MakeShaderStandard(RENDERER.ShadowMapMaterial->Shader); - RESOURCE_MANAGER.MakeMaterialStandard(RENDERER.ShadowMapMaterial); - - RENDERER.ShadowMapMaterialInstanced = RESOURCE_MANAGER.CreateMaterial("shadowMapMaterialInstanced"); - RENDERER.ShadowMapMaterialInstanced->Shader = RESOURCE_MANAGER.CreateShader("FEShadowMapShaderInstanced", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//ShadowMapMaterial//FE_ShadowMap_INSTANCED_VS.glsl").c_str()).c_str(), - RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//ShadowMapMaterial//FE_ShadowMap_FS.glsl").c_str()).c_str()); - RENDERER.ShadowMapMaterialInstanced->Shader->SetID("5634765B2E2A05321A182D1A"/*"FEShadowMapShaderInstanced"*/); - - RESOURCE_MANAGER.MakeShaderStandard(RENDERER.ShadowMapMaterialInstanced->Shader); - RESOURCE_MANAGER.MakeMaterialStandard(RENDERER.ShadowMapMaterialInstanced); + // Early initialization of the systems. + TRANSFORM_SYSTEM; + LIGHT_SYSTEM; + CAMERA_SYSTEM; + INSTANCED_RENDERING_SYSTEM; + TERRAIN_SYSTEM; + SKY_DOME_SYSTEM; + PREFAB_INSTANCE_SYSTEM; + VIRTUAL_UI_SYSTEM; + NATIVE_SCRIPT_SYSTEM; } void FEngine::SetWindowCaption(const std::string NewCaption) @@ -316,111 +175,17 @@ void FEngine::AddWindowCloseCallback(void(*Func)()) APPLICATION.GetMainWindow()->AddOnCloseCallback(Func); } -void FEngine::AddKeyCallback(void(*Func)(int, int, int, int)) -{ - if (Func != nullptr) - ClientKeyButtonCallbacks.push_back(Func); -} - -void FEngine::AddMouseButtonCallback(void(*Func)(int, int, int)) -{ - if (Func != nullptr) - ClientMouseButtonCallbacks.push_back(Func); -} - -void FEngine::AddMouseMoveCallback(void(*Func)(double, double)) -{ - if (Func != nullptr) - ClientMouseMoveCallbacks.push_back(Func); -} - void FEngine::WindowResizeCallback(const int Width, const int Height) { - ENGINE.WindowW = Width; - ENGINE.WindowH = Height; - - if (RenderTargetMode == FE_GLFW_MODE) - { - ENGINE.RenderTargetW = Width; - ENGINE.RenderTargetH = Height; - - RenderTargetResize(); - } - for (size_t i = 0; i < ENGINE.ClientWindowResizeCallbacks.size(); i++) { if (ENGINE.ClientWindowResizeCallbacks[i] == nullptr) continue; - ENGINE.ClientWindowResizeCallbacks[i](ENGINE.WindowW, ENGINE.WindowH); + ENGINE.ClientWindowResizeCallbacks[i](Width, Height); } } -void FEngine::MouseButtonCallback(const int Button, const int Action, const int Mods) -{ - for (size_t i = 0; i < ENGINE.ClientMouseButtonCallbacks.size(); i++) - { - if (ENGINE.ClientMouseButtonCallbacks[i] == nullptr) - continue; - - ENGINE.ClientMouseButtonCallbacks[i](Button, Action, Mods); - } -} - -void FEngine::MouseMoveCallback(double Xpos, double Ypos) -{ - if (RenderTargetMode == FE_CUSTOM_MODE) - { - Xpos -= RenderTargetXShift; - Ypos -= RenderTargetYShift; - } - - for (size_t i = 0; i < ENGINE.ClientMouseMoveCallbacks.size(); i++) - { - if (ENGINE.ClientMouseMoveCallbacks[i] == nullptr) - continue; - - ENGINE.ClientMouseMoveCallbacks[i](Xpos, Ypos); - } - - ENGINE.CurrentCamera->MouseMoveInput(Xpos, Ypos); - - ENGINE.MouseX = Xpos; - ENGINE.MouseY = Ypos; -} - -void FEngine::KeyButtonCallback(const int Key, const int Scancode, const int Action, const int Mods) -{ - for (size_t i = 0; i < ENGINE.ClientKeyButtonCallbacks.size(); i++) - { - if (ENGINE.ClientKeyButtonCallbacks[i] == nullptr) - continue; - ENGINE.ClientKeyButtonCallbacks[i](Key, Scancode, Action, Mods); - } - - ENGINE.CurrentCamera->KeyboardInput(Key, Scancode, Action, Mods); -} - -void FEngine::SetCamera(FEBasicCamera* NewCamera) -{ - CurrentCamera = NewCamera; -} - -FEBasicCamera* FEngine::GetCamera() -{ - return CurrentCamera; -} - -int FEngine::GetWindowWidth() -{ - return WindowW; -} - -int FEngine::GetWindowHeight() -{ - return WindowH; -} - void FEngine::RenderTo(FEFramebuffer* RenderTo) { RenderTo->Bind(); @@ -443,8 +208,8 @@ FEPostProcess* FEngine::CreatePostProcess(const std::string Name, int ScreenWidt { if (ScreenWidth < 2 || ScreenHeight < 2) { - ScreenWidth = GetRenderTargetWidth(); - ScreenHeight = GetRenderTargetHeight(); + ScreenWidth = ENGINE.GetDefaultViewport()->GetWidth(); + ScreenHeight = ENGINE.GetDefaultViewport()->GetHeight(); } return RESOURCE_MANAGER.CreatePostProcess(ScreenWidth, ScreenHeight, Name); @@ -455,323 +220,236 @@ void FEngine::Terminate() APPLICATION.Close(); } -void FEngine::TakeScreenshot(const char* FileName) +void FEngine::SaveScreenshot(std::string FileName, FEScene* SceneToWorkWith) { - RENDERER.TakeScreenshot(FileName, GetRenderTargetWidth(), GetRenderTargetHeight()); + RENDERER.SaveScreenshot(FileName, SceneToWorkWith); } -void FEngine::ResetCamera() +void FEngine::DropCallback(const int Count, const char** Paths) { - CurrentCamera->Reset(); - CurrentCamera->SetAspectRatio(static_cast(GetRenderTargetWidth()) / static_cast(GetRenderTargetHeight())); + for (size_t i = 0; i < ENGINE.ClientDropCallbacks.size(); i++) + { + if (ENGINE.ClientDropCallbacks[i] == nullptr) + continue; + + ENGINE.ClientDropCallbacks[i](Count, Paths); + } } -glm::dvec3 FEngine::ConstructMouseRay() +void FEngine::AddDropCallback(void(*Func)(int, const char**)) { - glm::dvec2 NormalizedMouseCoords; - NormalizedMouseCoords.x = (2.0f * MouseX) / GetRenderTargetWidth() - 1; - NormalizedMouseCoords.y = 1.0f - (2.0f * (MouseY)) / GetRenderTargetHeight(); - - const glm::dvec4 ClipCoords = glm::dvec4(NormalizedMouseCoords.x, NormalizedMouseCoords.y, -1.0, 1.0); - glm::dvec4 EyeCoords = glm::inverse(GetCamera()->GetProjectionMatrix()) * ClipCoords; - EyeCoords.z = -1.0f; - EyeCoords.w = 0.0f; - glm::dvec3 WorldRay = glm::inverse(GetCamera()->GetViewMatrix()) * EyeCoords; - WorldRay = glm::normalize(WorldRay); - - return WorldRay; + if (Func != nullptr) + ClientDropCallbacks.push_back(Func); } -FE_RENDER_TARGET_MODE FEngine::GetRenderTargetMode() +bool FEngine::IsVsyncEnabled() { - return RenderTargetMode; + return bVsyncEnabled; } -void FEngine::SetRenderTargetMode(const FE_RENDER_TARGET_MODE NewMode) +void FEngine::SetVsyncEnabled(bool NewValue) { - if (RenderTargetMode != NewMode && NewMode == FE_GLFW_MODE) + bVsyncEnabled = NewValue; + if (bVsyncEnabled) { - RenderTargetMode = NewMode; - int WindowWidth, WindowHeight; - APPLICATION.GetMainWindow()->GetSize(&WindowWidth, &WindowHeight); - WindowResizeCallback(WindowWidth, WindowHeight); + glfwSwapInterval(1); } else { - RenderTargetMode = NewMode; + glfwSwapInterval(0); } } -void FEngine::SetRenderTargetSize(const int Width, const int Height) -{ - if (Width <= 0 || Height <= 0 || RenderTargetMode == FE_GLFW_MODE) - return; - - bool NeedReInitialization = false; - if (RenderTargetW != Width || RenderTargetH != Height) - NeedReInitialization = true; - - RenderTargetW = Width; - RenderTargetH = Height; - - if (NeedReInitialization) - RenderTargetResize(); -} - -int FEngine::GetRenderTargetWidth() -{ - return RenderTargetW; -} - -int FEngine::GetRenderTargetHeight() +void FEngine::DisableVR() { - return RenderTargetH; + bVRActive = false; + RENDERER.bVRActive = false; } -void FEngine::RenderTargetResize() +bool FEngine::EnableVR() { - ENGINE.CurrentCamera->SetAspectRatio(static_cast(ENGINE.RenderTargetW) / static_cast(ENGINE.RenderTargetH)); - - RENDERER.RenderTargetResize(ENGINE.RenderTargetW, ENGINE.RenderTargetH); - - if (!ENGINE.bSimplifiedRendering) + if (!bVRInitializedCorrectly) { - // ************************************ Bloom ************************************ - FEPostProcess* BloomEffect = ENGINE.CreatePostProcess("bloom", static_cast(ENGINE.RenderTargetW / 4.0f), static_cast(ENGINE.RenderTargetH / 4.0f)); - BloomEffect->SetID("451C48791871283D372C5938"/*"bloom"*/); - - BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_SCENE_HDR_COLOR, RESOURCE_MANAGER.GetShader("0C19574118676C2E5645200E"/*"FEBloomThreshold"*/))); - - BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, RESOURCE_MANAGER.GetShader("7F3E4F5C130B537F0846274F"/*"FEBloomBlur"*/))); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(0.0f, 1.0f), "FEBlurDirection")); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(5.0f, "BloomSize")); - - BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, RESOURCE_MANAGER.GetShader("7F3E4F5C130B537F0846274F"/*"FEBloomBlur"*/))); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(1.0f, 0.0f), "FEBlurDirection")); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(5.0f, "BloomSize")); - - BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, RESOURCE_MANAGER.GetShader("7F3E4F5C130B537F0846274F"/*"FEBloomBlur"*/))); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(0.0f, 1.0f), "FEBlurDirection")); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(1.0f, "BloomSize")); - - BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, RESOURCE_MANAGER.GetShader("7F3E4F5C130B537F0846274F"/*"FEBloomBlur"*/))); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(1.0f, 0.0f), "FEBlurDirection")); - BloomEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(1.0f, "BloomSize")); - - BloomEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, FE_POST_PROCESS_SCENE_HDR_COLOR}, RESOURCE_MANAGER.GetShader("1833272551376C2E5645200E"/*"FEBloomComposition"*/))); - - RENDERER.AddPostProcess(BloomEffect); - // ************************************ Bloom END ************************************ - - // ************************************ gammaHDR ************************************ - FEPostProcess* GammaHDR = ENGINE.CreatePostProcess("GammaAndHDR", ENGINE.RenderTargetW, ENGINE.RenderTargetH); - GammaHDR->SetID("2374462A7B0E78141B5F5D79"/*"GammaAndHDR"*/); - GammaHDR->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, RESOURCE_MANAGER.GetShader("3417497A5E0C0C2A07456E44"/*"FEGammaAndHDRShader"*/))); - RENDERER.AddPostProcess(GammaHDR); - // ************************************ gammaHDR END ************************************ - - // ************************************ FXAA ************************************ - FEPostProcess* FEFXAAEffect = ENGINE.CreatePostProcess("FE_FXAA", ENGINE.RenderTargetW, ENGINE.RenderTargetH); - FEFXAAEffect->SetID("0A3F10643F06525D70016070"/*"FE_FXAA"*/); - FEFXAAEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, RESOURCE_MANAGER.GetShader("1E69744A10604C2A1221426B"/*"FEFXAAShader"*/))); - FEFXAAEffect->Stages.back()->Shader->UpdateParameterData("FXAATextuxelSize", glm::vec2(1.0f / ENGINE.RenderTargetW, 1.0f / ENGINE.RenderTargetH)); - RENDERER.AddPostProcess(FEFXAAEffect); - - //#fix for now after gamma correction I assume that texture output should be GL_RGB but in future it should be changeable. - RENDERER.PostProcessEffects.back()->ReplaceOutTexture(0, RESOURCE_MANAGER.CreateTexture(GL_RGB, GL_RGB, ENGINE.RenderTargetW, ENGINE.RenderTargetH)); - // ************************************ FXAA END ************************************ - - // ************************************ DOF ************************************ - FEPostProcess* DOFEffect = ENGINE.CreatePostProcess("DOF"); - DOFEffect->SetID("217C4E80482B6C650D7B492F"/*"DOF"*/); - - DOFEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, FE_POST_PROCESS_SCENE_DEPTH}, RESOURCE_MANAGER.GetShader("7800253C244442155D0F3C7B"/*"DOF"*/))); - DOFEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(0.0f, 1.0f), "FEBlurDirection")); - DOFEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, FE_POST_PROCESS_SCENE_DEPTH}, RESOURCE_MANAGER.GetShader("7800253C244442155D0F3C7B"/*"DOF"*/))); - DOFEffect->Stages.back()->StageSpecificUniforms.push_back(FEShaderParam(glm::vec2(1.0f, 0.0f), "FEBlurDirection")); - RENDERER.AddPostProcess(DOFEffect); - // ************************************ DOF END ************************************ - - // ************************************ chromaticAberrationEffect ************************************ - FEPostProcess* ChromaticAberrationEffect = ENGINE.CreatePostProcess("chromaticAberration"); - ChromaticAberrationEffect->SetID("506D804162647749060C3E68"/*"chromaticAberration"*/); - ChromaticAberrationEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0 }, RESOURCE_MANAGER.GetShader("9A41665B5E2B05321A332D09"/*"chromaticAberrationShader"*/))); - RENDERER.AddPostProcess(ChromaticAberrationEffect); - //#fix for now after gamma correction I assume that texture output should be GL_RGB but in future it should be changeable. - RENDERER.PostProcessEffects.back()->ReplaceOutTexture(0, RESOURCE_MANAGER.CreateTexture(GL_RGB, GL_RGB, ENGINE.RenderTargetW, ENGINE.RenderTargetH)); - // ************************************ chromaticAberrationEffect END ************************************ + bVRInitializedCorrectly = OpenXR_MANAGER.Init(APPLICATION.GetMainWindow()->GetTitle()); } - for (size_t i = 0; i < ENGINE.ClientRenderTargetResizeCallbacks.size(); i++) + if (bVRInitializedCorrectly) { - if (ENGINE.ClientRenderTargetResizeCallbacks[i] == nullptr) - continue; - ENGINE.ClientRenderTargetResizeCallbacks[i](ENGINE.RenderTargetW, ENGINE.RenderTargetH); + bVRActive = true; + RENDERER.bVRActive = true; + RENDERER.UpdateVRRenderTargetSize(static_cast(OpenXR_MANAGER.EyeResolution().x), static_cast(OpenXR_MANAGER.EyeResolution().y)); + } + else + { + bVRActive = false; + RENDERER.bVRActive = false; } -} - -void FEngine::AddRenderTargetResizeCallback(void(*Func)(int, int)) -{ - if (Func != nullptr) - ClientRenderTargetResizeCallbacks.push_back(Func); -} -inline int FEngine::GetRenderTargetXShift() -{ - return RenderTargetXShift; + return bVRActive; } -void FEngine::SetRenderTargetXShift(const int NewRenderTargetXShift) +bool FEngine::IsVRInitializedCorrectly() { - RenderTargetXShift = NewRenderTargetXShift; + return bVRInitializedCorrectly; } -inline int FEngine::GetRenderTargetYShift() +bool FEngine::IsVREnabled() { - return RenderTargetYShift; + return bVRActive; } -void FEngine::SetRenderTargetYShift(const int NewRenderTargetYShift) +void FEngine::AddOnAfterUpdateCallback(std::function Callback) { - RenderTargetYShift = NewRenderTargetYShift; + OnAfterUpdateCallbacks.push_back(Callback); } -void FEngine::RenderTargetCenterForCamera(FEFreeCamera* Camera) +std::string FEngine::CreateViewport(ImGuiWindow* ImGuiWindowPointer) { - int CenterX, CenterY = 0; - int ShiftX, ShiftY = 0; - - int xpos, ypos; - APPLICATION.GetMainWindow()->GetPosition(&xpos, &ypos); - - if (RenderTargetMode == FE_GLFW_MODE) + for (size_t i = 0; i < Viewports.size(); i++) { - CenterX = xpos + (WindowW / 2); - CenterY = ypos + (WindowH / 2); - - ShiftX = xpos; - ShiftY = ypos; + if (Viewports[i]->WindowHandle == ImGuiWindowPointer) + return Viewports[i]->ID; } - else if (RenderTargetMode == FE_CUSTOM_MODE) - { - CenterX = xpos + RenderTargetXShift + (RenderTargetW / 2); - CenterY = ypos + RenderTargetYShift + (RenderTargetH / 2); - ShiftX = RenderTargetXShift + xpos; - ShiftY = RenderTargetYShift + ypos; - } + FEViewport* NewViewport = new FEViewport(); + NewViewport->Type = FE_VIEWPORT_IMGUI_WINDOW; + NewViewport->WindowHandle = ImGuiWindowPointer; - Camera->SetRenderTargetCenterX(CenterX); - Camera->SetRenderTargetCenterY(CenterY); + Viewports.push_back(NewViewport); - Camera->SetRenderTargetShiftX(ShiftX); - Camera->SetRenderTargetShiftY(ShiftY); + return NewViewport->ID; } -void FEngine::DropCallback(const int Count, const char** Paths) +std::string FEngine::CreateViewport(FEWindow* FEWindowPointer) { - for (size_t i = 0; i < ENGINE.ClientDropCallbacks.size(); i++) + for (size_t i = 0; i < Viewports.size(); i++) { - if (ENGINE.ClientDropCallbacks[i] == nullptr) - continue; - - ENGINE.ClientDropCallbacks[i](Count, Paths); + if (Viewports[i]->WindowHandle == FEWindowPointer) + return Viewports[i]->ID; } -} -void FEngine::AddDropCallback(void(*Func)(int, const char**)) -{ - if (Func != nullptr) - ClientDropCallbacks.push_back(Func); + FEViewport* NewViewport = new FEViewport(); + NewViewport->Type = FE_VIEWPORT_FEWINDOW; + NewViewport->WindowHandle = FEWindowPointer; + + Viewports.push_back(NewViewport); + + return NewViewport->ID; } -void FEngine::MouseScrollCallback(const double Xoffset, const double Yoffset) +FEViewport* FEngine::GetViewport(std::string ViewportID) { - for (size_t i = 0; i < ENGINE.ClientMouseScrollCallbacks.size(); i++) + for (size_t i = 0; i < Viewports.size(); i++) { - if (ENGINE.ClientMouseScrollCallbacks[i] == nullptr) - continue; - - ENGINE.ClientMouseScrollCallbacks[i](Xoffset, Yoffset); + if (Viewports[i]->ID == ViewportID) + return Viewports[i]; } - ENGINE.CurrentCamera->MouseScrollInput(Xoffset, Yoffset); + return nullptr; } -glm::vec4 FEngine::GetClearColor() +void FEngine::ViewportCheckForModificationIndividual(FEViewport* ViewPort, bool& bMoved, bool& bResize) { - return CurrentClearColor; -} + bMoved = false; + bResize = false; -void FEngine::SetClearColor(glm::vec4 ClearColor) -{ - CurrentClearColor = ClearColor; - glClearColor(ClearColor.x, ClearColor.y, ClearColor.z, ClearColor.w); -} + switch (ViewPort->Type) + { + case FE_VIEWPORT_VIRTUAL: + return; -bool FEngine::IsSimplifiedRenderingModeActive() -{ - return bSimplifiedRendering; -} + case FE_VIEWPORT_OS_WINDOW: + return; -void FEngine::ActivateSimplifiedRenderingMode() -{ - bSimplifiedRendering = true; - RENDERER.bSimplifiedRendering = true; -} + case FE_VIEWPORT_GLFW_WINDOW: + return; + + case FE_VIEWPORT_FEWINDOW: + { + FEWindow* Window = static_cast(ViewPort->WindowHandle); -bool FEngine::IsVsyncEnabled() -{ - return bVsyncEnabled; -} + if (ViewPort->X != 0 || ViewPort->Y != 0) + bMoved = true; -void FEngine::SetVsyncEnabled(bool NewValue) -{ - bVsyncEnabled = NewValue; - if (bVsyncEnabled) - { - glfwSwapInterval(1); - } - else - { - glfwSwapInterval(0); + ViewPort->X = 0; + ViewPort->Y = 0; + + if (ViewPort->Width != Window->GetWidth() || ViewPort->Height != Window->GetHeight()) + bResize = true; + + ViewPort->Width = Window->GetWidth(); + ViewPort->Height = Window->GetHeight(); + + return; + } + + case FE_VIEWPORT_IMGUI_WINDOW: + { + ImGuiWindow* Window = static_cast(ViewPort->WindowHandle); + + if (ViewPort->X != static_cast(Window->ContentRegionRect.GetTL().x) || ViewPort->Y != static_cast(Window->ContentRegionRect.GetTL().y)) + bMoved = true; + + ViewPort->X = static_cast(Window->ContentRegionRect.GetTL().x); + ViewPort->Y = static_cast(Window->ContentRegionRect.GetTL().y); + + if (ViewPort->Width != static_cast(Window->ContentRegionRect.GetWidth()) || ViewPort->Height != static_cast(Window->ContentRegionRect.GetHeight())) + bResize = true; + + ViewPort->Width = static_cast(Window->ContentRegionRect.GetWidth()); + ViewPort->Height = static_cast(Window->ContentRegionRect.GetHeight()); + + return; + } } } -void FEngine::DisableVR() +void FEngine::AddOnViewportMovedCallback(std::function Callback) { - bVRActive = false; - RENDERER.bVRActive = false; + OnViewportMovedCallbacks.push_back(Callback); } -bool FEngine::EnableVR() +void FEngine::ViewportCheckForModification() { - if (!bVRInitializedCorrectly) + for (size_t i = 0; i < Viewports.size(); i++) { - bVRInitializedCorrectly = OpenXR_MANAGER.Init(WindowTitle); - } + bool bMoved, bResize; + ViewportCheckForModificationIndividual(Viewports[i], bMoved, bResize); - if (bVRInitializedCorrectly) - { - bVRActive = true; - RENDERER.bVRActive = true; - RENDERER.UpdateVRRenderTargetSize(static_cast(OpenXR_MANAGER.EyeResolution().x), static_cast(OpenXR_MANAGER.EyeResolution().y)); - } - else - { - bVRActive = false; - RENDERER.bVRActive = false; + if (bMoved) + { + for (size_t j = 0; j < OnViewportMovedCallbacks.size(); j++) + { + if (OnViewportMovedCallbacks[j] != nullptr) + OnViewportMovedCallbacks[j](Viewports[i]->ID); + } + } + + if (bResize) + { + for (size_t j = 0; j < OnViewportResizeCallbacks.size(); j++) + { + if (OnViewportResizeCallbacks[j] != nullptr) + OnViewportResizeCallbacks[j](Viewports[i]->ID); + } + } } +} - return bVRActive; +void FEngine::AddOnViewportResizeCallback(std::function Callback) +{ + OnViewportResizeCallbacks.push_back(Callback); } -bool FEngine::IsVRInitializedCorrectly() +FEViewport* FEngine::GetDefaultViewport() { - return bVRInitializedCorrectly; + if (Viewports.size() == 0) + return nullptr; + + return Viewports[0]; } -bool FEngine::IsVREnabled() +unsigned long long FEngine::GetCurrentFrameIndex() { - return bVRActive; + return CurentFrameIndex; } \ No newline at end of file diff --git a/FEngine.h b/FEngine.h index 9ddc849..8281cc3 100644 --- a/FEngine.h +++ b/FEngine.h @@ -5,17 +5,13 @@ namespace FocalEngine { - enum FE_RENDER_TARGET_MODE - { - FE_GLFW_MODE = 0, - FE_CUSTOM_MODE = 1, - }; - - class FEngine + class FOCAL_ENGINE_API FEngine { public: SINGLETON_PUBLIC_PART(FEngine) + std::string GetEngineBuildVersion(); + void InitWindow(int Width = 1920 * 2, int Height = 1080 * 2, std::string WindowTitle = "FEWindow"); void BeginFrame(bool InternalCall = false); @@ -26,52 +22,21 @@ namespace FocalEngine bool IsNotTerminated(); void Terminate(); - void SetCamera(FEBasicCamera* NewCamera); - FEBasicCamera* GetCamera(); - void SetWindowCaption(std::string NewCaption); - void AddRenderTargetResizeCallback(void(*Func)(int, int)); void AddWindowResizeCallback(void(*Func)(int, int)); void AddWindowCloseCallback(void(*Func)()); - void AddKeyCallback(void(*Func)(int, int, int, int)); - void AddMouseButtonCallback(void(*Func)(int, int, int)); - void AddMouseMoveCallback(void(*Func)(double, double)); void AddDropCallback(void(*Func)(int, const char**)); - int GetWindowWidth(); - int GetWindowHeight(); - void RenderTo(FEFramebuffer* RenderTo); double GetCpuTime(); double GetGpuTime(); FEPostProcess* CreatePostProcess(std::string Name, int ScreenWidth = -1, int ScreenHeight = -1); - void TakeScreenshot(const char* FileName); - - void ResetCamera(); - - glm::dvec3 ConstructMouseRay(); - - inline FE_RENDER_TARGET_MODE GetRenderTargetMode(); - void SetRenderTargetMode(FE_RENDER_TARGET_MODE NewMode); - - inline int GetRenderTargetWidth(); - void SetRenderTargetSize(int Width, int Height); - inline int GetRenderTargetHeight(); - - inline int GetRenderTargetXShift(); - void SetRenderTargetXShift(int NewRenderTargetXShift); - inline int GetRenderTargetYShift(); - void SetRenderTargetYShift(int NewRenderTargetYShift); - - void RenderTargetCenterForCamera(FEFreeCamera* Camera); - - glm::vec4 GetClearColor(); - void SetClearColor(glm::vec4 ClearColor); + void SaveScreenshot(std::string FileName, FEScene* SceneToWorkWith); - bool IsSimplifiedRenderingModeActive(); - void ActivateSimplifiedRenderingMode(); + FEViewport* GetDefaultViewport(); + FEViewport* GetViewport(std::string ViewportID); bool IsVsyncEnabled(); void SetVsyncEnabled(bool NewValue); @@ -80,55 +45,52 @@ namespace FocalEngine bool EnableVR(); bool IsVRInitializedCorrectly(); bool IsVREnabled(); + + void AddOnAfterUpdateCallback(std::function Callback); + + // Returns Viewport ID + std::string CreateViewport(ImGuiWindow* ImGuiWindowPointer); + // Returns Viewport ID + std::string CreateViewport(FEWindow* FEWindowPointer); + + void AddOnViewportMovedCallback(std::function Callback); + void AddOnViewportResizeCallback(std::function Callback); + + unsigned long long GetCurrentFrameIndex(); private: SINGLETON_PRIVATE_PART(FEngine) - int WindowW; - int WindowH; - std::string WindowTitle; - - double CPUTime, GPUTime; - double MouseX, MouseY; + double CPUTime = 0.0, GPUTime = 0.0; + double CurrentDeltaTime = 0.0; bool bSimplifiedRendering = false; bool bVsyncEnabled = true; bool bVRInitializedCorrectly = false; bool bVRActive = false; - const glm::vec4 DefaultClearColor = glm::vec4(0.55f, 0.73f, 0.87f, 1.0f); - const glm::vec4 DefaultGammaCorrectedClearColor = glm::vec4(pow(0.55f, -2.2f), pow(0.73f, -2.2f), pow(0.87f, -2.2f), 1.0f); - glm::vec4 CurrentClearColor = DefaultGammaCorrectedClearColor; - - static FE_RENDER_TARGET_MODE RenderTargetMode; - int RenderTargetW; - int RenderTargetH; - static int RenderTargetXShift; - static int RenderTargetYShift; - static void RenderTargetResize(); - - std::vector ClientRenderTargetResizeCallbacks; - //void(*clientRenderTargetResizeCallbackImpl)(int, int) = nullptr; - static void WindowResizeCallback(int Width, int Height); std::vector ClientWindowResizeCallbacks; - static void MouseButtonCallback(int Button, int Action, int Mods); - std::vector ClientMouseButtonCallbacks; - - static void MouseMoveCallback(double Xpos, double Ypos); - std::vector ClientMouseMoveCallbacks; - - static void KeyButtonCallback(int Key, int Scancode, int Action, int Mods); - std::vector ClientKeyButtonCallbacks; - static void DropCallback(int Count, const char** Paths); std::vector ClientDropCallbacks; - static void MouseScrollCallback(double Xoffset, double Yoffset); - std::vector ClientMouseScrollCallbacks; + void InternalUpdate(); + std::vector> OnAfterUpdateCallbacks; + + std::vector Viewports; + std::vector> OnViewportMovedCallbacks; + std::vector> OnViewportResizeCallbacks; + + void ViewportCheckForModification(); + void ViewportCheckForModificationIndividual(FEViewport* ViewPort, bool& bMoved, bool& bResize); - FEBasicCamera* CurrentCamera = nullptr; + unsigned long long CurentFrameIndex = 0; }; - #define ENGINE FEngine::getInstance() +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetEngine(); + #define ENGINE (*static_cast(GetEngine())) +#else + #define ENGINE FEngine::GetInstance() +#endif } \ No newline at end of file diff --git a/README.md b/README.md index c9a8bbb..4a08dfd 100644 --- a/README.md +++ b/README.md @@ -2,36 +2,176 @@ ![build](https://github.com/Azzinoth/FocalEngine/actions/workflows/Build.yml/badge.svg?branch=master) -Focal Engine is a personal project of a 3D rendering engine, showcasing an array of custom shaders, materials, and rendering techniques. +Focal Engine is an open-source, modular real-time 3D rendering and game engine designed for building interactive 3D applications, games, and scientific visualizations. It excels at handling complex geospatial data and GPU-driven rendering, empowering developers and researchers to create sophisticated graphical solutions efficiently. + +âš ï¸ **Currently in Alpha**: Expect breaking changes and ongoing development. ![3D scene in the Focal Engine](https://github.com/Azzinoth/FocalEngine/blob/media/1.png) ![3D scene in the Focal Engine](https://github.com/Azzinoth/FocalEngine/blob/media/2.png) -![3D scene in the Focal Engine](https://github.com/Azzinoth/FocalEngine/blob/media/3.png) ![3D scene in the Focal Engine](https://github.com/Azzinoth/FocalEngine/blob/media/4.png) -![3D scene in the Focal Engine](https://github.com/Azzinoth/FocalEngine/blob/media/5.png) +![3D scene in the Focal Engine](https://github.com/Azzinoth/FocalEngine/blob/media/6.png) + +## Table of Contents +- [Focal Engine Ecosystem](#focal-engine-ecosystem) +- [Projects Using Focal Engine](#projects-using-focal-engine) +- [Features](#features) + - [Graphical Features](#graphical-features) + - [Point Cloud Handling](#advanced-point-cloud-handling) + - [Terrain](#terrain) + - [Performance](#performance) + - [VR Support](#vr-support) + - [Entity Component System (ECS)](#entity-component-system-ecs) + - [Prefab System](#prefab-system) + - [C++ Scripting](#c-scripting) +- [Editor](#editor-overview) +- [Build and Setup](#build-system) +- [Testing](#testing) +- [Example Application](#simple-application-example) +- [Third-Party Licenses](#third-party-licenses) + +## Focal Engine Ecosystem + +The Focal Engine project consists of four modular components that work together to provide a complete development environment: + +[Basic Application Module](https://github.com/Azzinoth/FEBasicApplication) - A foundation layer for OpenGL and ImGui applications that provides essential utilities including time measurement, thread pooling, logging, TCP networking, and profiling capabilities. + +[Visual Node System](https://github.com/Azzinoth/VisualNodeSystem) - A framework for creating visual node-based interfaces with features like zoom, reroute nodes, group comments, and JSON serialization, ideal for material editors and visual scripting. + +Focal Engine (this repository) - The engine with all core functionality. + +[Focal Engine Editor](https://github.com/Azzinoth/FocalEngineEditor) - A comprehensive editor for the engine. + +This modularity makes it easier to include just the engine in applications that don't need the editor's complexity. It also simplifies the implementation of export functionality in the editor, allowing users to compile their projects into standalone executable applications with all necessary resources. + +## Projects Using Focal Engine + +[HabiCAT 3D](https://github.com/Azzinoth/HabiCAT3D) - An open-source software that implements novel algorithms for generating multi-scale complexity metrics maps(like rugosity, fractal dimension, vector dispersion and others) for complex 3D habitat models. ## Features +### Graphical Features + - Physically-based rendering - High dynamic range (HDR) rendering internally - Gamma correction - Deferred shading - Screen-Space Ambient Occlusion (SSAO) - Cascaded soft shadows -- Fast Approximate Anti-Aliasing (FXAA) - Sky with atmospheric scattering - Compressed textures support with multi-threaded loading +- Fast Approximate Anti-Aliasing (FXAA) +- Dynamic render scale adjustment +- Camera temporal jitter and partial motion vector calculations, needed for future TAA and third-party upscalers +- Each camera has its own unique rendering pipeline with customizable settings + +### Advanced Point Cloud Handling + +The Focal Engine provides specialized capabilities for working with large-scale point cloud data: -## Terrain +- LAS/LAZ File Format Support + + âš ï¸ Work in progress âš ï¸ + +- High-Performance Rendering: Support for real-time visualization of massive point clouds (hundreds of millions to billions of points) + âš ï¸ Work in progress âš ï¸ + +- GPU-Accelerated Editing: Tools for manipulating and editing huge point clouds directly in GPU memory + âš ï¸ Work in progress âš ï¸ + +### Terrain - Chunked terrain with tessellation for LOD - Frustum culling of sub chunks - Up to 8 terrain layer materials - Each layer can define foliage spawn rules +### VR Support + +The Focal Engine leverages OpenXR integration to provide support for a wide range of VR headsets and controllers. The engine has a virtual UI system specifically designed for comfortable and intuitive interaction in VR space. + +![3D scene in the Focal Engine](https://github.com/Azzinoth/FocalEngine/blob/media/8.png) + +### Entity Component System (ECS) + +Focal Engine employs EnTT ECS for optimized performance and flexible architecture. Easily compose entities from modular components, enabling scalable and maintainable code. + +#### Prefab System + +Easily define reusable entities or groups of entities as Prefabs. Prefabs encapsulate entities and their component configurations as scenes, allowing you to instantiate complex object setups multiple times efficiently. Prefabs simplify asset management and accelerate level design. + +### C++ Scripting: + +Attach custom scripts directly to entities for rapid prototyping, modular gameplay logic, and efficient runtime performance. + ## Performance -All instanced entities will have a pass with multiple computer shaders that perform LOD level calculations, Frustum and Occlusion culling entirely on the GPU. +Focal Engine features a GPU-driven rendering pipeline (âš ï¸ Work in progress âš ï¸: currently not working with all entity components) that includes GPU Frustum Culling and GPU Occlusion Culling. The latter utilizes a custom Hierarchical Z-Buffer (HZB). Both culling techniques are implemented using compute shaders. These advanced optimization techniques enable the Focal Engine to render a substantial number of objects per scene (up to millions, depending on object types). + +### Profiling + +Engine submodule includes a profiling system that provides detailed analysis of CPU utilization across threads. This system is designed to handle complex, highly multithreaded workloads, allowing to identify and address performance bottlenecks. + +![3D scene in the Focal Engine](https://github.com/Azzinoth/FocalEngine/blob/media/7.png) + +## Editor Overview + +The Focal Engine Editor provides project management capabilities through its project browser window: + +![Project Browser](https://github.com/Azzinoth/FocalEngineEditor/blob/media/Project%20Browser.png) + +The editor interface includes several key windows: + +1. **Scene Entities**: Displays entities in a hierarchical graph structure. +2. **Scene/Scenes**: Supports multiple scene windows simultaneously. Prefabs are handled as simplified scenes for modular design. +3. **Inspector**: Provides list of components of the selected entity for viewing and editing. +4. **Content Browser**: Serves as an explorer for the project's virtual file system with structured access to all resources. +5. **Editor Cameras**: Provides settings for editor-specific cameras. While game cameras (entities with camera components) are only functional in game mode, editor cameras allow navigation through scenes even when no game camera is present. +6. **Log Window**: Displays filtered categories of warnings, errors, and informational messages for debugging. + +![Log window](https://github.com/Azzinoth/FocalEngineEditor/blob/media/Log%20window.png) + +Standard editor layout: + +![Editor](https://github.com/Azzinoth/FocalEngineEditor/blob/media/Editor.png) + +The Material Editor employs the [Visual Node System](https://github.com/Azzinoth/VisualNodeSystem) to enhance editing intuitiveness. + +![Material Editor](https://github.com/Azzinoth/FocalEngineEditor/blob/media/Material%20editor.png) + +Terrain editing is facilitated with different brushes (sculpt, smooth, paint layers). Each terrain can support up to eight material layers, with each layer capable of enforcing the type of foliage that can spawn on it. + +![Terrain](https://github.com/Azzinoth/FocalEngineEditor/blob/media/Terrain.png) + +## Build System + +âš ï¸ Work in progress âš ï¸ + +The Focal Engine Editor includes functionality to compile projects into standalone executable (.exe) files, along with packaged resource files containing all necessary scripts, assets, and resources. This allows for easy distribution of completed applications without requiring the editor or engine development environment. + + +## Shader debugging + +Users can edit shaders on-the-fly in the built-in shader editor and debug compilation errors: + +![Shader Compilation errors](https://github.com/Azzinoth/FocalEngineEditor/blob/media/Shader%20Compilation%20errors.png) + +âš ï¸ Work in progress âš ï¸ + +In addition, the editor supports real-time retrieval of shader variable values from the GPU. (Please note that not all variables are supported and further testing is needed for this feature.) + +![Shader values read back](https://github.com/Azzinoth/FocalEngineEditor/blob/media/Shader%20values%20read%20back.png) + +## Testing + +During the development of the engine and its tools, inadequate testing was identified as a source of setbacks. To address this, a multi-layered testing approach has been implemented: + +1. **Continuous Integration** - GitHub Actions run on every push as a sanity check to ensure the engine compiles successfully. + +2. **Unit Testing** - Using Google Test framework to verify individual components. Currently limited to scene graph testing, with plans to expand coverage to more subsystems over time. + +3. **Visual Testing Platform** - Due to the graphical nature of the engine, unit tests and GitHub Actions are insufficient for comprehensive testing. A specialized testing platform is being developed that will evaluate the engine's visual output and UI functionality. + +This comprehensive testing strategy will enable more confident feature iteration and establish a proper regression testing pipeline. ## Building the Project for Visual Studio (Windows) as .lib @@ -53,9 +193,6 @@ git submodule update --init --recursive cmake CMakeLists.txt ``` -## Scene Editor -This repository only contains the engine itself to maintain modularity. For the full experience, it is recommended to use the [Focal Engine Editor](https://github.com/Azzinoth/FocalEngineEditor). - ## Simple Application Example An example of a simple application that integrates the Engine: [Example](https://github.com/Azzinoth/FocalEngineAppExample). @@ -71,4 +208,8 @@ This project uses the following third-party libraries: 4) **stb**: This library is licensed under the MIT License. The full license text can be found at [stb's GitHub repository](https://github.com/nothings/stb/blob/master/LICENSE). -5) **OpenXR**: This library is licensed under Apache 2.0 License. The full license text can be found at [OpenXR's GitHub repository](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/main/LICENSE). \ No newline at end of file +5) **OpenXR**: This library is licensed under Apache 2.0 License. The full license text can be found at [OpenXR's GitHub repository](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/main/LICENSE). + +6) **googletest**: This library is under the BSD-3-Clause license. The full license text can be found at [googletest's GitHub repository](https://github.com/google/googletest?tab=BSD-3-Clause-1-ov-file). + +7) **EnTT**: This library is under the MIT License. The full license text can be found at [EnTT's GitHub repository](https://github.com/skypjack/entt?tab=MIT-1-ov-file). \ No newline at end of file diff --git a/Renderer/FEEntity.cpp b/Renderer/FEEntity.cpp deleted file mode 100644 index 736f21f..0000000 --- a/Renderer/FEEntity.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "FEEntity.h" -using namespace FocalEngine; - -FEEntity::FEEntity() : FEObject(FE_ENTITY, "") -{ -} - -FEEntity::FEEntity(FEPrefab* Prefab, std::string Name) : FEObject(FE_ENTITY, Name) -{ - this->Prefab = Prefab; - SetName(Name); - if (Prefab != nullptr) - EntityAABB = Prefab->GetAABB(); -} - -FEEntity::~FEEntity() -{ -} - -void FEEntity::Render() -{ - //FE_GL_ERROR(glBindVertexArray(gameModel->mesh->getVaoID())); - //if ((gameModel->mesh->vertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); - //if ((gameModel->mesh->vertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); - //if ((gameModel->mesh->vertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); - //if ((gameModel->mesh->vertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); - //if ((gameModel->mesh->vertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); - //if ((gameModel->mesh->vertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); - - //if ((gameModel->mesh->vertexAttributes & FE_INDEX) == FE_INDEX) - // FE_GL_ERROR(glDrawElements(GL_TRIANGLES, gameModel->mesh->getVertexCount(), GL_UNSIGNED_INT, 0)); - //if ((gameModel->mesh->vertexAttributes & FE_INDEX) != FE_INDEX) - // FE_GL_ERROR(glDrawArrays(GL_TRIANGLES, 0, gameModel->mesh->getVertexCount())); - - //// could it be that this disabled part is only slowing engine down without any profit ? - ///*if ((gameModel->mesh->vertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glDisableVertexAttribArray(0)); - //if ((gameModel->mesh->vertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glDisableVertexAttribArray(1)); - //if ((gameModel->mesh->vertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glDisableVertexAttribArray(2)); - //if ((gameModel->mesh->vertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glDisableVertexAttribArray(3)); - //if ((gameModel->mesh->vertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glDisableVertexAttribArray(4)); - //if ((gameModel->mesh->vertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glDisableVertexAttribArray(5));*/ - //FE_GL_ERROR(glBindVertexArray(0)); -} - -bool FEEntity::IsVisible() const -{ - return bVisible; -} - -void FEEntity::SetVisibility(const bool NewValue) -{ - bVisible = NewValue; -} - -FEAABB FEEntity::GetAABB() -{ - if (Transform.bDirtyFlag) - { - EntityAABB = Prefab->GetAABB().Transform(Transform.GetTransformMatrix()); - Transform.bDirtyFlag = false; - } - - return EntityAABB; -} - -bool FEEntity::IsCastShadows() const -{ - return bCastShadows; -} - -void FEEntity::SetCastShadows(const bool NewValue) -{ - bCastShadows = NewValue; -} - -bool FEEntity::IsReceivingShadows() const -{ - return bReceiveShadows; -} - -void FEEntity::SetReceivingShadows(const bool NewValue) -{ - bReceiveShadows = NewValue; -} - -bool FEEntity::IsPostprocessApplied() const -{ - return bApplyPostprocess; -} - -void FEEntity::SetIsPostprocessApplied(const bool NewValue) -{ - bApplyPostprocess = NewValue; -} - -void FEEntity::SetWireframeMode(const bool NewValue) -{ - bWireframeMode = NewValue; -} - -bool FEEntity::IsWireframeMode() const -{ - return bWireframeMode; -} - -bool FEEntity::IsUniformLighting() const -{ - return bUniformLighting; -} - -void FEEntity::SetUniformLighting(bool NewValue) -{ - bUniformLighting = NewValue; -} \ No newline at end of file diff --git a/Renderer/FEEntityInstanced.cpp b/Renderer/FEEntityInstanced.cpp deleted file mode 100644 index 81c1bf5..0000000 --- a/Renderer/FEEntityInstanced.cpp +++ /dev/null @@ -1,917 +0,0 @@ -#include "FEEntityInstanced.h" - -using namespace FocalEngine; - -float FESpawnInfo::GetMinScale() -{ - return MinScale; -} - -void FESpawnInfo::SetMinScale(const float NewValue) -{ - if (NewValue >= MaxScale) - return; - - MinScale = NewValue; -} - -float FESpawnInfo::GetMaxScale() -{ - return MaxScale; -} - -void FESpawnInfo::SetMaxScale(const float NewValue) -{ - if (NewValue <= MinScale) - return; - - MaxScale = NewValue; -} - -float FESpawnInfo::GetPositionDeviation() -{ - const int IntegerPart = rand() % static_cast(Radius); - const float FractionalPart = static_cast((rand() % 100) / 100.0f); - float result = static_cast(IntegerPart) + FractionalPart; - - result -= Radius / 2.0f; - - return result; - //return (rand() % int(radius * 100)) / 100.0f - radius / 2.0f; -} - -float FESpawnInfo::GetScaleDeviation() -{ - //float finalDeviation = ((float(rand() % int(scaleDeviation * 10000)) / 10000.0f) - scaleDeviation / 2.0f); - - //float minScale = 0.5f; - //float maxScale = 1.5f; - const float FinalDeviation = MinScale + ((static_cast(rand() % static_cast((MaxScale - MinScale) * 10000)) / 10000.0f)); - - return FinalDeviation; -} - -int FESpawnInfo::GetRotaionDeviation(const glm::vec3 Axis) -{ - if (Axis.x > 0.0f) - { - const int rot = static_cast(360 * RotationDeviation.x); - if (rot == 0) - return 0; - return rand() % rot; - } - else if (Axis.y > 0.0f) - { - const int rot = static_cast(360 * RotationDeviation.y); - if (rot == 0) - return 0; - return rand() % rot; - } - else - { - const int rot = static_cast(360 * RotationDeviation.z); - if (rot == 0) - return 0; - return rand() % rot; - } -} - -FEInstanceModification::FEInstanceModification() -{ - -} - -FEEntityInstanced::FEEntityInstanced(FEPrefab* Prefab, const std::string Name) : FEEntity(Prefab, Name) -{ - SetType(FE_ENTITY_INSTANCED); - - Renderers.resize(Prefab->Components.size()); - for (int i = 0; i < Prefab->Components.size(); i++) - { - InitRender(i); - } - - Transform.SetScale(glm::vec3(1.0f)); -} - -void FEEntityInstanced::InitRender(const int Index) -{ - Renderers[Index] = new FEGameModelInstancedRenderer; - Renderers[Index]->LODCounts = new int[Prefab->Components[Index]->GameModel->GetMaxLODCount()]; - - Renderers[Index]->LODBuffers = new GLenum[Prefab->Components[Index]->GameModel->GetMaxLODCount()]; - for (size_t j = 0; j < Prefab->Components[Index]->GameModel->GetMaxLODCount(); j++) - { - FE_GL_ERROR(glGenBuffers(1, &Renderers[Index]->LODBuffers[j])); - } - - FE_GL_ERROR(glGenBuffers(1, &Renderers[Index]->SourceDataBuffer)); - FE_GL_ERROR(glGenBuffers(1, &Renderers[Index]->PositionsBuffer)); - FE_GL_ERROR(glGenBuffers(1, &Renderers[Index]->AABBSizesBuffer)); - FE_GL_ERROR(glGenBuffers(1, &Renderers[Index]->LODInfoBuffer)); - - Renderers[Index]->IndirectDrawsInfo = new FEDrawElementsIndirectCommand[4]; - for (size_t j = 0; j < Prefab->Components[Index]->GameModel->GetMaxLODCount(); j++) - { - Renderers[Index]->IndirectDrawsInfo[j].Count = Prefab->Components[Index]->GameModel->GetLODMesh(j) == nullptr ? 0 : Prefab->Components[Index]->GameModel->GetLODMesh(j)->GetVertexCount(); - Renderers[Index]->IndirectDrawsInfo[j].BaseInstance = 0; - Renderers[Index]->IndirectDrawsInfo[j].BaseVertex = 0; - Renderers[Index]->IndirectDrawsInfo[j].FirstIndex = 0; - Renderers[Index]->IndirectDrawsInfo[j].PrimCount = 0; - } - - FE_GL_ERROR(glGenBuffers(1, &Renderers[Index]->IndirectDrawInfoBuffer)); - FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, Renderers[Index]->IndirectDrawInfoBuffer)); - FE_GL_ERROR(glBufferStorage(GL_DRAW_INDIRECT_BUFFER, sizeof(FEDrawElementsIndirectCommand) * 4, Renderers[Index]->IndirectDrawsInfo, GL_MAP_READ_BIT)); - FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0)); - - Renderers[Index]->InstancedMatricesLOD.resize(Prefab->Components[Index]->GameModel->GetMaxLODCount()); -} - -FEEntityInstanced::~FEEntityInstanced() -{ - delete[] Renderers.back()->LODCounts; - delete[] Renderers.back()->IndirectDrawsInfo; -} - -void FEEntityInstanced::Render(const int SubGameModel) -{ - if (InstanceCount == 0) - return; - - if (Renderers.size() <= SubGameModel || Prefab->Components.size() <= SubGameModel) - return; - - CheckDirtyFlag(SubGameModel); - - for (size_t i = 0; i < Prefab->Components[SubGameModel]->GameModel->GetMaxLODCount(); i++) - { - if (Prefab->Components[SubGameModel]->GameModel->IsLODBillboard(i)) - break; - - if (Prefab->Components[SubGameModel]->GameModel->GetLODMesh(i) != nullptr) - { - if (Renderers[SubGameModel]->LODBuffers[i] == 0) - break; - - FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, Renderers[SubGameModel]->LODBuffers[i])); - - FE_GL_ERROR(glBindVertexArray(Prefab->Components[SubGameModel]->GameModel->GetLODMesh(i)->GetVaoID())); - - if ((Prefab->Components[SubGameModel]->GameModel->GetLODMesh(i)->VertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); - if ((Prefab->Components[SubGameModel]->GameModel->GetLODMesh(i)->VertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); - if ((Prefab->Components[SubGameModel]->GameModel->GetLODMesh(i)->VertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); - if ((Prefab->Components[SubGameModel]->GameModel->GetLODMesh(i)->VertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); - if ((Prefab->Components[SubGameModel]->GameModel->GetLODMesh(i)->VertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); - if ((Prefab->Components[SubGameModel]->GameModel->GetLODMesh(i)->VertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); - - FE_GL_ERROR(glEnableVertexAttribArray(6)); - FE_GL_ERROR(glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), static_cast(0))); - FE_GL_ERROR(glEnableVertexAttribArray(7)); - FE_GL_ERROR(glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)))); - FE_GL_ERROR(glEnableVertexAttribArray(8)); - FE_GL_ERROR(glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)))); - FE_GL_ERROR(glEnableVertexAttribArray(9)); - FE_GL_ERROR(glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)))); - - FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, Renderers[SubGameModel]->IndirectDrawInfoBuffer)); - FE_GL_ERROR(glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, (GLvoid*)(i * sizeof(FEDrawElementsIndirectCommand)))); - - FE_GL_ERROR(glBindVertexArray(0)); - FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, 0)); - } - } - - if (CullingType == FE_CULLING_NONE) - { - FE_GL_ERROR(glBindVertexArray(Prefab->Components[SubGameModel]->GameModel->Mesh->GetVaoID())); - if ((Prefab->Components[SubGameModel]->GameModel->Mesh->VertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); - if ((Prefab->Components[SubGameModel]->GameModel->Mesh->VertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); - if ((Prefab->Components[SubGameModel]->GameModel->Mesh->VertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); - if ((Prefab->Components[SubGameModel]->GameModel->Mesh->VertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); - if ((Prefab->Components[SubGameModel]->GameModel->Mesh->VertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); - if ((Prefab->Components[SubGameModel]->GameModel->Mesh->VertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); - - FE_GL_ERROR(glEnableVertexAttribArray(6)); - FE_GL_ERROR(glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), static_cast(nullptr))); - FE_GL_ERROR(glEnableVertexAttribArray(7)); - FE_GL_ERROR(glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)))); - FE_GL_ERROR(glEnableVertexAttribArray(8)); - FE_GL_ERROR(glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)))); - FE_GL_ERROR(glEnableVertexAttribArray(9)); - FE_GL_ERROR(glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)))); - - FE_GL_ERROR(glDrawElementsInstanced(GL_TRIANGLES, Prefab->Components[SubGameModel]->GameModel->Mesh->GetVertexCount(), GL_UNSIGNED_INT, nullptr, static_cast(InstanceCount))); - - FE_GL_ERROR(glBindVertexArray(0)); - } -} - -void FEEntityInstanced::RenderOnlyBillbords(glm::vec3 CameraPosition) -{ - for (size_t i = 0; i < Renderers.size(); i++) - { - for (size_t j = 0; j < Prefab->Components[i]->GameModel->GetMaxLODCount(); j++) - { - if (Prefab->Components[i]->GameModel->IsLODBillboard(j) && Prefab->Components[i]->GameModel->GetLODMesh(j) != nullptr) - { - if (Renderers[i]->LODBuffers[j] == 0) - break; - - FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, Renderers[i]->LODBuffers[j])); - - FE_GL_ERROR(glBindVertexArray(Prefab->Components[i]->GameModel->GetLODMesh(j)->GetVaoID())); - - if ((Prefab->Components[i]->GameModel->GetLODMesh(j)->VertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); - if ((Prefab->Components[i]->GameModel->GetLODMesh(j)->VertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); - if ((Prefab->Components[i]->GameModel->GetLODMesh(j)->VertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); - if ((Prefab->Components[i]->GameModel->GetLODMesh(j)->VertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); - if ((Prefab->Components[i]->GameModel->GetLODMesh(j)->VertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); - if ((Prefab->Components[i]->GameModel->GetLODMesh(j)->VertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); - - FE_GL_ERROR(glEnableVertexAttribArray(6)); - FE_GL_ERROR(glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), static_cast(nullptr))); - FE_GL_ERROR(glEnableVertexAttribArray(7)); - FE_GL_ERROR(glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)))); - FE_GL_ERROR(glEnableVertexAttribArray(8)); - FE_GL_ERROR(glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)))); - FE_GL_ERROR(glEnableVertexAttribArray(9)); - FE_GL_ERROR(glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)))); - - FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, Renderers[i]->IndirectDrawInfoBuffer)); - FE_GL_ERROR(glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, (GLvoid*)(j * sizeof(FEDrawElementsIndirectCommand)))); - - FE_GL_ERROR(glBindVertexArray(0)); - FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, 0)); - - break; - } - } - } -} - -void FEEntityInstanced::UpdateBuffers() -{ - for (size_t i = 0; i < Prefab->Components.size(); i++) - { - if (Renderers[i]->InstancedBuffer != 0) - { - glDeleteBuffers(1, &Renderers[i]->InstancedBuffer); - } - - FE_GL_ERROR(glGenBuffers(1, &Renderers[i]->InstancedBuffer)); - FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, Renderers[i]->InstancedBuffer)); - FE_GL_ERROR(glBufferData(GL_ARRAY_BUFFER, InstanceCount * sizeof(glm::mat4), Renderers[i]->InstancedMatrices.data(), GL_DYNAMIC_DRAW)); - - const unsigned int VAO = Prefab->Components[i]->GameModel->Mesh->GetVaoID(); - FE_GL_ERROR(glBindVertexArray(VAO)); - // set attribute pointers for matrix (4 times vec4) - FE_GL_ERROR(glEnableVertexAttribArray(6)); - FE_GL_ERROR(glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), static_cast(nullptr))); - FE_GL_ERROR(glEnableVertexAttribArray(7)); - FE_GL_ERROR(glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)))); - FE_GL_ERROR(glEnableVertexAttribArray(8)); - FE_GL_ERROR(glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)))); - FE_GL_ERROR(glEnableVertexAttribArray(9)); - FE_GL_ERROR(glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)))); - - FE_GL_ERROR(glVertexAttribDivisor(6, 1)); - FE_GL_ERROR(glVertexAttribDivisor(7, 1)); - FE_GL_ERROR(glVertexAttribDivisor(8, 1)); - FE_GL_ERROR(glVertexAttribDivisor(9, 1)); - - FE_GL_ERROR(glBindVertexArray(0)); - - for (size_t j = 0; j < Prefab->Components[i]->GameModel->GetMaxLODCount(); j++) - { - if (Prefab->Components[i]->GameModel->GetLODMesh(j) != nullptr) - { - const unsigned int VAO = Prefab->Components[i]->GameModel->GetLODMesh(j)->GetVaoID(); - FE_GL_ERROR(glBindVertexArray(VAO)); - // set attribute pointers for matrix (4 times vec4) - FE_GL_ERROR(glEnableVertexAttribArray(6)); - FE_GL_ERROR(glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), static_cast(nullptr))); - FE_GL_ERROR(glEnableVertexAttribArray(7)); - FE_GL_ERROR(glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)))); - FE_GL_ERROR(glEnableVertexAttribArray(8)); - FE_GL_ERROR(glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)))); - FE_GL_ERROR(glEnableVertexAttribArray(9)); - FE_GL_ERROR(glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)))); - - FE_GL_ERROR(glVertexAttribDivisor(6, 1)); - FE_GL_ERROR(glVertexAttribDivisor(7, 1)); - FE_GL_ERROR(glVertexAttribDivisor(8, 1)); - FE_GL_ERROR(glVertexAttribDivisor(9, 1)); - - FE_GL_ERROR(glBindVertexArray(0)); - } - } - - FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, 0)); - - Renderers[i]->AllInstancesAABB = FEAABB(); - for (size_t j = 0; j < InstanceCount; j++) - { - glm::mat4 MatWithoutTranslate = Renderers[i]->TransformedInstancedMatrices[j]; - MatWithoutTranslate[3][0] -= Transform.Position.x; - MatWithoutTranslate[3][1] -= Transform.Position.y; - MatWithoutTranslate[3][2] -= Transform.Position.z; - - Renderers[i]->AllInstancesAABB = Renderers[i]->AllInstancesAABB.Merge(Prefab->Components[i]->GameModel->Mesh->AABB.Transform(MatWithoutTranslate)); - } - } - - Transform.bDirtyFlag = true; - GetAABB(); -} - -void FEEntityInstanced::Clear() -{ - InstanceCount = 0; - - for (size_t i = 0; i < Renderers.size(); i++) - { - delete[] Renderers[i]->LODCounts; - - Renderers[i]->InstancedAABBSizes.resize(0); - Renderers[i]->InstancedMatrices.resize(0); - Renderers[i]->TransformedInstancedMatrices.resize(0); - Renderers[i]->InstancePositions.resize(0); - - Renderers[i]->LODCounts = new int[Prefab->Components[i]->GameModel->GetMaxLODCount()]; - for (size_t j = 0; j < Prefab->Components[i]->GameModel->GetMaxLODCount(); j++) - { - Renderers[i]->LODCounts[j] = 0; - } - - Renderers[i]->InstancedMatricesLOD.resize(Prefab->Components[i]->GameModel->GetMaxLODCount()); - } - - Transform.SetScale(glm::vec3(1.0f)); - Modifications.clear(); -} - -void FEEntityInstanced::AddInstanceInternal(const glm::mat4 InstanceMatrix) -{ - for (size_t i = 0; i < Prefab->Components.size(); i++) - { - Renderers[i]->InstancedAABBSizes.push_back(-FEAABB(Prefab->Components[i]->GameModel->GetMesh()->GetAABB(), InstanceMatrix).LongestAxisLength); - Renderers[i]->InstancedMatrices.push_back(InstanceMatrix); - Renderers[i]->TransformedInstancedMatrices.push_back(Transform.TransformMatrix * InstanceMatrix); - Renderers[i]->InstancePositions.push_back(Renderers[i]->TransformedInstancedMatrices.back()[3]); - - for (size_t j = 0; j < Prefab->Components[i]->GameModel->GetMaxLODCount(); j++) - { - Renderers[i]->InstancedMatricesLOD[j].resize(InstanceCount); - } - } - - InstanceCount++; - SetDirtyFlag(true); -} - -void FEEntityInstanced::AddInstances(const glm::mat4* InstanceMatrix, const size_t Count) -{ - for (size_t i = 0; i < Prefab->Components.size(); i++) - { - const size_t StartIndex = Renderers[i]->InstancedAABBSizes.size(); - - Renderers[i]->InstancedAABBSizes.resize(Renderers[i]->InstancedAABBSizes.size() + Count); - const FEAABB OriginalAABB = Prefab->Components[i]->GameModel->GetMesh()->GetAABB(); - Renderers[i]->InstancedMatrices.resize(Renderers[i]->InstancedMatrices.size() + Count); - Renderers[i]->TransformedInstancedMatrices.resize(Renderers[i]->TransformedInstancedMatrices.size() + Count); - Renderers[i]->InstancePositions.resize(Renderers[i]->InstancePositions.size() + Count); - - for (size_t j = StartIndex; j < Count; j++) - { - Renderers[i]->InstancedAABBSizes[j] = -FEAABB(OriginalAABB, InstanceMatrix[j]).LongestAxisLength; - Renderers[i]->InstancedMatrices[j] = InstanceMatrix[j]; - Renderers[i]->TransformedInstancedMatrices[j] = Transform.TransformMatrix * InstanceMatrix[j]; - - Renderers[i]->InstancePositions[j] = Renderers[i]->TransformedInstancedMatrices[j][3]; - if (i == 0) - InstanceCount++; - } - - for (size_t j = 0; j < Prefab->Components[i]->GameModel->GetMaxLODCount(); j++) - { - Renderers[i]->InstancedMatricesLOD[j].resize(InstanceCount); - } - } - - SetDirtyFlag(true); -} - -FEAABB FEEntityInstanced::GetAABB() -{ - if (Transform.bDirtyFlag) - { - if (Prefab != nullptr) - { - if (!Renderers.empty()) - { - EntityAABB = Renderers[0]->AllInstancesAABB.Transform(Transform.GetTransformMatrix()); - for (size_t i = 1; i < Renderers.size(); i++) - { - EntityAABB = EntityAABB.Merge(Renderers[i]->AllInstancesAABB.Transform(Transform.GetTransformMatrix())); - } - } - else - { - EntityAABB = FEAABB(); - } - } - - UpdateMatrices(); - Transform.bDirtyFlag = false; - } - - return EntityAABB; -} - -int FEEntityInstanced::GetInstanceCount() -{ - return static_cast(InstanceCount); -} - -void FEEntityInstanced::UpdateMatrices() -{ - for (size_t i = 0; i < Renderers.size(); i++) - { - if (Renderers[i]->InstancedMatrices.size() != Renderers[i]->TransformedInstancedMatrices.size()) - { - LOG.Add("instancedMatrices size and transformedInstancedMatrices size is not equal!", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - for (size_t j = 0; j < Renderers[i]->InstancedMatrices.size(); j++) - { - Renderers[i]->TransformedInstancedMatrices[j] = Transform.TransformMatrix * Renderers[i]->InstancedMatrices[j]; - Renderers[i]->InstancePositions[j] = Renderers[i]->TransformedInstancedMatrices[j][3]; - } - - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, Renderers[i]->SourceDataBuffer)); - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstanceCount * sizeof(glm::mat4), Renderers[i]->TransformedInstancedMatrices.data(), GL_DYNAMIC_DRAW)); - - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, Renderers[i]->PositionsBuffer)); - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstanceCount * sizeof(float) * 3, Renderers[i]->InstancePositions.data(), GL_DYNAMIC_DRAW)); - } - - //initializeGPUCulling(); -} - -bool FEEntityInstanced::Populate(FESpawnInfo SpawnInfo) -{ - if (SpawnInfo.Radius <= 0.0f || SpawnInfo.ObjectCount < 1 || SpawnInfo.ObjectCount > 1000000 || Prefab == nullptr) - return false; - - this->SpawnInfo = SpawnInfo; - srand(SpawnInfo.Seed); - - const glm::vec3 Min = Prefab->GetAABB().GetMin(); - const glm::vec3 Max = Prefab->GetAABB().GetMax(); - - float YSize = sqrt((Max.y - Min.y) * (Max.y - Min.y)); - YSize *= Prefab->Components[0]->GameModel->GetScaleFactor(); - - std::vector NewMats; - NewMats.resize(SpawnInfo.ObjectCount); - - for (size_t i = 0; i < NewMats.size(); i++) - { - glm::mat4 NewMat = glm::mat4(1.0); - // spawner transformation would be taken in account later so consider center in 0 - float x = SpawnInfo.GetPositionDeviation(); - float z = SpawnInfo.GetPositionDeviation(); - float y = SpawnInfo.GetPositionDeviation(); - - if (TerrainToSnap != nullptr) - { - y = std::invoke(GetTerrainY, TerrainToSnap, glm::vec2(Transform.Position.x + x, Transform.Position.z + z)); - - if (TerrainLayer != -1 && y != -FLT_MAX) - { - const float LayerIntensity = std::invoke(GetTerrainLayerIntensity, TerrainToSnap, glm::vec2(Transform.Position.x + x, Transform.Position.z + z), TerrainLayer); - if (LayerIntensity < MinLayerIntensity) - y = -FLT_MAX; - } - - int CountOfTries = 0; - while (y == -FLT_MAX) - { - x = SpawnInfo.GetPositionDeviation(); - z = SpawnInfo.GetPositionDeviation(); - y = std::invoke(GetTerrainY, TerrainToSnap, glm::vec2(Transform.Position.x + x, Transform.Position.z + z)); - - if (TerrainLayer != -1 && y != -FLT_MAX) - { - const float LayerIntensity = std::invoke(GetTerrainLayerIntensity, TerrainToSnap, glm::vec2(Transform.Position.x + x, Transform.Position.z + z), TerrainLayer); - if (LayerIntensity < MinLayerIntensity) - y = -FLT_MAX; - } - - CountOfTries++; - if (CountOfTries > 300) - break; - } - - if (CountOfTries > 300) - { - y = Transform.Position.y + SpawnInfo.GetPositionDeviation(); - } - } - - NewMat = glm::translate(NewMat, glm::vec3(x, y, z)); - - NewMat = glm::rotate(NewMat, SpawnInfo.GetRotaionDeviation(glm::vec3(1, 0, 0)) * ANGLE_TORADIANS_COF, glm::vec3(1, 0, 0)); - NewMat = glm::rotate(NewMat, SpawnInfo.GetRotaionDeviation(glm::vec3(0, 1, 0)) * ANGLE_TORADIANS_COF, glm::vec3(0, 1, 0)); - NewMat = glm::rotate(NewMat, SpawnInfo.GetRotaionDeviation(glm::vec3(0, 0, 1)) * ANGLE_TORADIANS_COF, glm::vec3(0, 0, 1)); - - float FinalScale = Prefab->Components[0]->GameModel->GetScaleFactor() + Prefab->Components[0]->GameModel->GetScaleFactor() * SpawnInfo.GetScaleDeviation(); - if (FinalScale < 0.0f) - FinalScale = 0.01f; - NewMat = glm::scale(NewMat, glm::vec3(FinalScale)); - - NewMats[i] = NewMat; - } - AddInstances(NewMats.data(), NewMats.size()); - - if (TerrainToSnap != nullptr) - { - // terrain.y could be not 0.0f but here we should indicate 0.0f as Y. - Transform.SetPosition(glm::vec3(Transform.Position.x, 0.0f, Transform.Position.z)); - } - - srand(static_cast(time(nullptr))); - - SetDirtyFlag(true); - return true; -} - -FETerrain* FEEntityInstanced::GetSnappedToTerrain() -{ - return TerrainToSnap; -} - -void FEEntityInstanced::UpdateSelectModeAABBData() -{ - InstancedAABB.clear(); - InstancedAABB.resize(InstanceCount); - - for (size_t i = 0; i < InstanceCount; i++) - { - InstancedAABB[i] = FEAABB(Prefab->GetAABB(), Renderers[0]->TransformedInstancedMatrices[i]); - } - - SetDirtyFlag(true); -} - -bool FEEntityInstanced::IsSelectMode() -{ - return bSelectionMode; -} - -void FEEntityInstanced::SetSelectMode(const bool NewValue) -{ - if (NewValue) - UpdateSelectModeAABBData(); - - bSelectionMode = NewValue; -} - -void FEEntityInstanced::DeleteInstance(const size_t InstanceIndex) -{ - if (InstanceIndex < 0 || InstanceIndex >= Renderers[0]->InstancedMatrices.size()) - return; - - Modifications.push_back(FEInstanceModification(FE_CHANGE_DELETED, static_cast(InstanceIndex), glm::mat4())); - - InstanceCount--; - for (size_t i = 0; i < Renderers.size(); i++) - { - Renderers[i]->InstancedAABBSizes.erase(Renderers[i]->InstancedAABBSizes.begin() + InstanceIndex); - Renderers[i]->InstancedMatrices.erase(Renderers[i]->InstancedMatrices.begin() + InstanceIndex); - Renderers[i]->TransformedInstancedMatrices.erase(Renderers[i]->TransformedInstancedMatrices.begin() + InstanceIndex); - Renderers[i]->InstancePositions.erase(Renderers[i]->InstancePositions.begin() + InstanceIndex); - - for (size_t j = 0; j < Prefab->Components[i]->GameModel->GetMaxLODCount(); j++) - { - Renderers[i]->InstancedMatricesLOD[j].resize(InstanceCount); - } - } - - if (InstancedAABB.empty()) - { - UpdateSelectModeAABBData(); - } - else - { - InstancedAABB.erase(InstancedAABB.begin() + InstanceIndex); - } - - SetDirtyFlag(true); -} - -glm::mat4 FEEntityInstanced::GetTransformedInstancedMatrix(const size_t InstanceIndex) -{ - if (InstanceIndex < 0 || InstanceIndex >= Renderers[0]->TransformedInstancedMatrices.size()) - return glm::identity(); - - return Renderers[0]->TransformedInstancedMatrices[InstanceIndex]; -} - -void FEEntityInstanced::ModifyInstance(const size_t InstanceIndex, glm::mat4 NewMatrix) -{ - if (InstanceIndex < 0 || InstanceIndex >= Renderers[0]->TransformedInstancedMatrices.size()) - return; - - if (glm::all(glm::epsilonEqual(Renderers[0]->TransformedInstancedMatrices[InstanceIndex][0], NewMatrix[0], 0.001f)) && - glm::all(glm::epsilonEqual(Renderers[0]->TransformedInstancedMatrices[InstanceIndex][1], NewMatrix[1], 0.001f)) && - glm::all(glm::epsilonEqual(Renderers[0]->TransformedInstancedMatrices[InstanceIndex][2], NewMatrix[2], 0.001f)) && - glm::all(glm::epsilonEqual(Renderers[0]->TransformedInstancedMatrices[InstanceIndex][3], NewMatrix[3], 0.001f))) - return; - - if (!Modifications.empty() && Modifications.back().Index == InstanceIndex && Modifications.back().Type == FE_CHANGE_MODIFIED) - { - Modifications.back().Modification = NewMatrix; - } - else - { - Modifications.push_back(FEInstanceModification(FE_CHANGE_MODIFIED, static_cast(InstanceIndex), NewMatrix)); - } - - for (size_t i = 0; i < Renderers.size(); i++) - { - Renderers[i]->TransformedInstancedMatrices[InstanceIndex] = NewMatrix; - Renderers[i]->InstancedMatrices[InstanceIndex] = glm::inverse(Transform.GetTransformMatrix()) * NewMatrix; - - if (InstancedAABB.size() > InstanceIndex) - InstancedAABB[InstanceIndex] = FEAABB(Prefab->GetAABB(), NewMatrix); - Renderers[i]->InstancedAABBSizes[InstanceIndex] = -FEAABB(Prefab->GetAABB(), NewMatrix).LongestAxisLength; - } - - SetDirtyFlag(true); -} - -int FEEntityInstanced::GetSpawnModificationCount() -{ - return static_cast(Modifications.size()); -} - -std::vector FEEntityInstanced::GetSpawnModifications() -{ - return Modifications; -} - -void FEEntityInstanced::AddInstance(const glm::mat4 InstanceMatrix) -{ - AddInstanceInternal(glm::inverse(Transform.TransformMatrix) * InstanceMatrix); - - if (InstancedAABB.empty()) - { - UpdateSelectModeAABBData(); - } - else - { - InstancedAABB.push_back(FEAABB(Prefab->GetAABB(), Renderers[0]->TransformedInstancedMatrices.back())); - } - - Modifications.push_back(FEInstanceModification(FE_CHANGE_ADDED, static_cast(InstanceCount), InstanceMatrix)); - SetDirtyFlag(true); -} - -bool FEEntityInstanced::TryToSnapInstance(const size_t InstanceIndex) -{ - if (InstanceIndex < 0 || InstanceIndex >= Renderers[0]->TransformedInstancedMatrices.size() || TerrainToSnap == nullptr) - return false; - - if (!IsSelectMode()) - return false; - - const float y = std::invoke(GetTerrainY, TerrainToSnap, glm::vec2(Renderers[0]->TransformedInstancedMatrices[InstanceIndex][3][0], Renderers[0]->TransformedInstancedMatrices[InstanceIndex][3][2])); - if (y == -FLT_MAX) - return false; - - const float LayerIntensity = std::invoke(GetTerrainLayerIntensity, TerrainToSnap, glm::vec2(Renderers[0]->TransformedInstancedMatrices[InstanceIndex][3][0], Renderers[0]->TransformedInstancedMatrices[InstanceIndex][3][2]), TerrainLayer); - if (LayerIntensity < MinLayerIntensity) - return false; - - if (abs(Renderers[0]->TransformedInstancedMatrices[InstanceIndex][3][1] - y) < 0.01f) - return true; - - glm::mat4 Copy = Renderers[0]->TransformedInstancedMatrices[InstanceIndex]; - Copy[3][1] = y; - ModifyInstance(InstanceIndex, Copy); - SetDirtyFlag(true); - return true; -} - -void FEEntityInstanced::InitializeGPUCulling() -{ - for (size_t i = 0; i < Prefab->Components.size(); i++) - { - if (Renderers[i]->SourceDataBuffer != 0) - { - FE_GL_ERROR(glDeleteBuffers(1, &Renderers[i]->SourceDataBuffer)); - FE_GL_ERROR(glGenBuffers(1, &Renderers[i]->SourceDataBuffer)); - } - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, Renderers[i]->SourceDataBuffer)); - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstanceCount * sizeof(glm::mat4), Renderers[i]->TransformedInstancedMatrices.data(), GL_DYNAMIC_DRAW)); - - if (Renderers[i]->PositionsBuffer != 0) - { - FE_GL_ERROR(glDeleteBuffers(1, &Renderers[i]->PositionsBuffer)); - FE_GL_ERROR(glGenBuffers(1, &Renderers[i]->PositionsBuffer)); - } - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, Renderers[i]->PositionsBuffer)); - - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstanceCount * sizeof(float) * 3, Renderers[i]->InstancePositions.data(), GL_DYNAMIC_DRAW)); - - if (Renderers[i]->LODBuffers[0] != 0) - { - FE_GL_ERROR(glDeleteBuffers(1, &Renderers[i]->LODBuffers[0])); - FE_GL_ERROR(glGenBuffers(1, &Renderers[i]->LODBuffers[0])); - } - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, Renderers[i]->LODBuffers[0])); - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstanceCount * sizeof(glm::mat4), nullptr, GL_DYNAMIC_DRAW)); - - if (Renderers[i]->LODBuffers[1] != 0) - { - FE_GL_ERROR(glDeleteBuffers(1, &Renderers[i]->LODBuffers[1])); - FE_GL_ERROR(glGenBuffers(1, &Renderers[i]->LODBuffers[1])); - } - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, Renderers[i]->LODBuffers[1])); - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstanceCount * sizeof(glm::mat4), nullptr, GL_DYNAMIC_DRAW)); - - if (Renderers[i]->LODBuffers[2] != 0) - { - FE_GL_ERROR(glDeleteBuffers(1, &Renderers[i]->LODBuffers[2])); - FE_GL_ERROR(glGenBuffers(1, &Renderers[i]->LODBuffers[2])); - } - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, Renderers[i]->LODBuffers[2])); - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstanceCount * sizeof(glm::mat4), nullptr, GL_DYNAMIC_DRAW)); - - if (Renderers[i]->LODBuffers[3] != 0) - { - FE_GL_ERROR(glDeleteBuffers(1, &Renderers[i]->LODBuffers[3])); - FE_GL_ERROR(glGenBuffers(1, &Renderers[i]->LODBuffers[3])); - } - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 9, Renderers[i]->LODBuffers[3])); - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstanceCount * sizeof(glm::mat4), nullptr, GL_DYNAMIC_DRAW)); - - if (Renderers[i]->AABBSizesBuffer != 0) - { - FE_GL_ERROR(glDeleteBuffers(1, &Renderers[i]->AABBSizesBuffer)); - FE_GL_ERROR(glGenBuffers(1, &Renderers[i]->AABBSizesBuffer)); - } - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, Renderers[i]->AABBSizesBuffer)); - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstanceCount * sizeof(float), Renderers[i]->InstancedAABBSizes.data(), GL_DYNAMIC_DRAW)); - - std::vector LODInfoData; - LODInfoData.push_back(Prefab->Components[i]->GameModel->GetCullDistance()); - LODInfoData.push_back(Prefab->Components[i]->GameModel->GetLODMaxDrawDistance(0)); - LODInfoData.push_back(Prefab->Components[i]->GameModel->GetLODMaxDrawDistance(1)); - LODInfoData.push_back(Prefab->Components[i]->GameModel->GetLODMaxDrawDistance(2)); - - // does it have billboard ? - unsigned int BillboardIndex = 5; - for (size_t j = 0; j < Prefab->Components[i]->GameModel->GetMaxLODCount(); j++) - { - if (Prefab->Components[i]->GameModel->IsLODBillboard(j) && Prefab->Components[i]->GameModel->GetLODMesh(j) != nullptr) - { - BillboardIndex = static_cast(j); - } - } - - LODInfoData.push_back(static_cast(BillboardIndex)); - // this should not be here, instead normal of plane should align with vector to camera - LODInfoData.push_back(1.5708f * 3.0f + Prefab->Components[i]->GameModel->GetBillboardZeroRotaion() * ANGLE_TORADIANS_COF); - LODInfoData.push_back(static_cast(InstanceCount)); - - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, Renderers[i]->LODInfoBuffer)); - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, 7 * sizeof(float), LODInfoData.data(), GL_DYNAMIC_DRAW)); - - if (Renderers[i]->IndirectDrawsInfo == nullptr) - Renderers[i]->IndirectDrawsInfo = new FEDrawElementsIndirectCommand[4]; - for (size_t j = 0; j < Prefab->Components[i]->GameModel->GetMaxLODCount(); j++) - { - Renderers[i]->IndirectDrawsInfo[j].Count = Prefab->Components[i]->GameModel->GetLODMesh(j) == nullptr ? 0 : Prefab->Components[i]->GameModel->GetLODMesh(j)->GetVertexCount(); - Renderers[i]->IndirectDrawsInfo[j].BaseInstance = 0; - Renderers[i]->IndirectDrawsInfo[j].BaseVertex = 0; - Renderers[i]->IndirectDrawsInfo[j].FirstIndex = 0; - Renderers[i]->IndirectDrawsInfo[j].PrimCount = 0; - } - - if (Renderers[i]->IndirectDrawInfoBuffer != 0) - { - FE_GL_ERROR(glDeleteBuffers(1, &Renderers[i]->IndirectDrawInfoBuffer)); - FE_GL_ERROR(glGenBuffers(1, &Renderers[i]->IndirectDrawInfoBuffer)); - } - - FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, Renderers[i]->IndirectDrawInfoBuffer)); - FE_GL_ERROR(glBufferStorage(GL_DRAW_INDIRECT_BUFFER, sizeof(FEDrawElementsIndirectCommand) * 4, Renderers[i]->IndirectDrawsInfo, GL_MAP_READ_BIT)); - FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0)); - } -} - -void FEEntityInstanced::SnapToTerrain(FETerrain* Terrain, float(FETerrain::* GetTerrainY)(glm::vec2)) -{ - TerrainToSnap = Terrain; - this->GetTerrainY = GetTerrainY; -} - -void FEEntityInstanced::UnSnapFromTerrain() -{ - TerrainToSnap = nullptr; -} - -void FEEntityInstanced::ClearRenderers() -{ - if (Prefab->Components.size() > Renderers.size()) - { - const int CountBefore = static_cast(Renderers.size()); - Renderers.resize(Prefab->Components.size()); - for (int i = CountBefore; i < Renderers.size(); i++) - { - InitRender(i); - - Renderers[i]->InstancedMatrices = Renderers[0]->InstancedMatrices; - Renderers[i]->TransformedInstancedMatrices = Renderers[0]->TransformedInstancedMatrices; - Renderers[i]->InstancePositions = Renderers[0]->InstancePositions; - } - } - else if (Prefab->Components.size() < Renderers.size()) - { - Renderers.erase(Renderers.begin() + Renderers.size() - 1, Renderers.end()); - } -} - -void FEEntityInstanced::CheckDirtyFlag(const int SubGameModel) -{ - if (Renderers[SubGameModel]->LastFramePrefab != Prefab || Prefab->IsDirty()) - { - SetDirtyFlag(true); - Renderers[SubGameModel]->LastFramePrefab = Prefab; - } - - for (int i = 0; i < Prefab->ComponentsCount(); i++) - { - if (Prefab->GetComponent(i)->GameModel->IsDirty() || Prefab->GetComponent(i)->Transform.IsDirty()) - { - SetDirtyFlag(true); - break; - } - } - - if (IsDirty()) - { - ClearRenderers(); - UpdateBuffers(); - SetDirtyFlag(false); - InitializeGPUCulling(); - //prefab->components[subGameModel]->gameModel->bDirtyFlag = false; - Prefab->SetDirtyFlag(false); - for (int i = 0; i < Prefab->ComponentsCount(); i++) - { - Prefab->GetComponent(i)->GameModel->SetDirtyFlag(false); - Prefab->GetComponent(i)->Transform.SetDirtyFlag(false); - } - } - - if (Transform.bDirtyFlag) - { - UpdateMatrices(); - } -} - -void FEEntityInstanced::ConnectToTerrainLayer(FETerrain* Terrain, const int LayerIndex, float(FETerrain::* GetTerrainLayerIntensity)(glm::vec2, int)) -{ - this->GetTerrainLayerIntensity = GetTerrainLayerIntensity; - TerrainLayer = LayerIndex; -} - -int FEEntityInstanced::GetTerrainLayer() -{ - return TerrainLayer; -} - -void FEEntityInstanced::UnConnectFromTerrainLayer() -{ - TerrainLayer = -1; -} - -float FEEntityInstanced::GetMinimalLayerIntensity() -{ - return MinLayerIntensity; -} - -void FEEntityInstanced::SetMinimalLayerIntensity(float NewValue) -{ - if (NewValue < 0.0001f) - NewValue = 0.0001f; - - if (NewValue > 1.0f) - NewValue = 1.0f; - - MinLayerIntensity = NewValue; -} \ No newline at end of file diff --git a/Renderer/FEEntityInstanced.h b/Renderer/FEEntityInstanced.h deleted file mode 100644 index 9b0713b..0000000 --- a/Renderer/FEEntityInstanced.h +++ /dev/null @@ -1,171 +0,0 @@ -#pragma once - -#ifndef FEENTITYINSTANCED_H -#define FEENTITYINSTANCED_H - -#include "FEEntity.h" - -namespace FocalEngine -{ - enum FE_INSTANCED_ENTITY_CHANGES_TYPE - { - FE_CHANGE_NONE = 0, - FE_CHANGE_DELETED = 1, - FE_CHANGE_MODIFIED = 2, - FE_CHANGE_ADDED = 3 - }; - - struct FEInstanceModification - { - FE_INSTANCED_ENTITY_CHANGES_TYPE Type = FE_CHANGE_NONE; - int Index = -1; - glm::mat4 Modification; - - FEInstanceModification(); - FEInstanceModification(const FE_INSTANCED_ENTITY_CHANGES_TYPE Type, const int Index, const glm::mat4 Modification) : Type(Type), Index(Index), Modification(Modification) {}; - }; - - struct FESpawnInfo - { - int Seed = 0; - int ObjectCount = 1; - float Radius = 1.0f; - - float GetMinScale(); - void SetMinScale(float NewValue); - - float GetMaxScale(); - void SetMaxScale(float NewValue); - - glm::vec3 RotationDeviation = glm::vec3(0.02f, 1.0f, 0.02f); - - float GetPositionDeviation(); - float GetScaleDeviation(); - int GetRotaionDeviation(glm::vec3 Axis); - - private: - float MinScale = 1.0f; - float MaxScale = 1.5f; - }; - - struct FEDrawElementsIndirectCommand - { - unsigned int Count; - unsigned int PrimCount; - unsigned int FirstIndex; - unsigned int BaseVertex; - unsigned int BaseInstance; - }; - - class FEEntityInstanced; - class FEGameModelInstancedRenderer - { - friend FERenderer; - friend FEEntityInstanced; - - std::vector> InstancedMatricesLOD; - - std::vector InstancePositions; - int* LODCounts; - - // GPU Culling - GLenum InstancedBuffer = 0; - GLenum* LODBuffers = nullptr; - - GLuint SourceDataBuffer = 0; - GLuint PositionsBuffer = 0; - GLuint AABBSizesBuffer = 0; - GLuint LODInfoBuffer = 0; - - FEDrawElementsIndirectCommand* IndirectDrawsInfo; - GLuint IndirectDrawInfoBuffer = 0; - - FEAABB AllInstancesAABB; - FEPrefab* LastFramePrefab = nullptr; - - std::vector InstancedMatrices; - std::vector TransformedInstancedMatrices; - std::vector InstancedAABBSizes; - }; - - class FEEntityInstanced : public FEEntity - { - friend FERenderer; - friend FETerrain; - friend FEScene; - public: - FEEntityInstanced(FEPrefab* Prefab, std::string Name); - ~FEEntityInstanced(); - - bool Populate(FESpawnInfo SpawnInfo); - void Render(int SubGameModel); - void RenderOnlyBillbords(glm::vec3 CameraPosition); - - FEAABB GetAABB() final; - void Clear(); - - size_t InstanceCount = 0; - - int GetInstanceCount(); - - FETerrain* GetSnappedToTerrain(); - int GetTerrainLayer(); - - float GetMinimalLayerIntensity(); - void SetMinimalLayerIntensity(float NewValue); - - FESpawnInfo SpawnInfo; - - void UpdateSelectModeAABBData(); - - // used only in editor select mode - std::vector InstancedAABB; - bool IsSelectMode(); - void SetSelectMode(bool NewValue); - - void DeleteInstance(size_t InstanceIndex); - glm::mat4 GetTransformedInstancedMatrix(size_t InstanceIndex); - void ModifyInstance(size_t InstanceIndex, glm::mat4 NewMatrix); - void AddInstance(glm::mat4 InstanceMatrix); - - bool TryToSnapInstance(size_t InstanceIndex); - - int GetSpawnModificationCount(); - std::vector GetSpawnModifications(); - - std::vector Modifications; - private: - std::vector Renderers; - - bool bSelectionMode = false; - - int CullingType = FE_CULLING_LODS; - - FETerrain* TerrainToSnap = nullptr; - int TerrainLayer = -1; - float MinLayerIntensity = 0.4f; - float(FETerrain::* GetTerrainY)(glm::vec2); - float(FETerrain::* GetTerrainLayerIntensity)(glm::vec2, int); - - void AddInstanceInternal(glm::mat4 InstanceMatrix); - void AddInstances(const glm::mat4* InstanceMatrix, size_t Count); - - void ClearRenderers(); - void InitRender(int Index); - - void UpdateBuffers(); - void UpdateMatrices(); - - void InitializeGPUCulling(); - - void SnapToTerrain(FETerrain* Terrain, float(FETerrain::* GetTerrainY)(glm::vec2)); - void UnSnapFromTerrain(); - - void ConnectToTerrainLayer(FETerrain* Terrain, int LayerIndex, float(FETerrain::* GetTerrainLayerIntensity)(glm::vec2, int)); - void UnConnectFromTerrainLayer(); - - void CheckDirtyFlag(int SubGameModel); - }; -} - -#endif FEENTITYINSTANCED_H \ No newline at end of file diff --git a/Renderer/FEFramebuffer.h b/Renderer/FEFramebuffer.h index 440d181..0b9a554 100644 --- a/Renderer/FEFramebuffer.h +++ b/Renderer/FEFramebuffer.h @@ -13,6 +13,7 @@ namespace FocalEngine class FEFramebuffer : public FEObject { + friend struct FEVirtualUIComponent; friend class FEVirtualUIContext; friend class FEngine; friend class FEResourceManager; diff --git a/Renderer/FEGameModel.h b/Renderer/FEGameModel.h index 5430882..379b776 100644 --- a/Renderer/FEGameModel.h +++ b/Renderer/FEGameModel.h @@ -28,7 +28,6 @@ namespace FocalEngine { friend FERenderer; friend FEPrefab; - friend FEEntityInstanced; public: FEGameModel(FEMesh* Mesh, FEMaterial* Material, std::string Name); ~FEGameModel(); diff --git a/Renderer/FELight.cpp b/Renderer/FELight.cpp deleted file mode 100644 index 29c9160..0000000 --- a/Renderer/FELight.cpp +++ /dev/null @@ -1,725 +0,0 @@ -#include "FELight.h" -using namespace FocalEngine; - -FELight::FELight(const FE_OBJECT_TYPE LightType) : FEObject(LightType, "") -{ -} - -FELight::~FELight() -{ -} - -glm::vec3 FELight::GetColor() -{ - return Color; -} - -void FELight::SetColor(const glm::vec3 NewValue) -{ - Color = NewValue; -} - -bool FELight::IsLightEnabled() -{ - return bEnabled; -} - -void FELight::SetLightEnabled(const bool NewValue) -{ - bEnabled = NewValue; -} - -float FELight::GetShadowBias() -{ - return ShadowBias; -} - -void FELight::SetShadowBias(const float NewValue) -{ - ShadowBias = NewValue; -} - -float FELight::GetIntensity() -{ - return Intensity; -} - -void FELight::SetIntensity(const float NewValue) -{ - Intensity = NewValue; -} - -float FELight::GetShadowBlurFactor() -{ - return ShadowBlurFactor; -} - -void FELight::SetShadowBlurFactor(float NewValue) -{ - if (NewValue < 0.0f) - NewValue = 0.0f; - - if (NewValue > 16.0f) - NewValue = 16.0f; - - ShadowBlurFactor = NewValue; -} - -bool FELight::IsCastShadows() -{ - return bCastShadows; -} - -void FELight::SetCastShadows(const bool NewValue) -{ - bCastShadows = NewValue; -} - -FECascadeData::FECascadeData() -{ - Frustum = new float* [6]; - for (size_t i = 0; i < 6; i++) - { - Frustum[i] = new float[4]; - } -} - -FECascadeData::~FECascadeData() -{ - for (size_t i = 0; i < 6; i++) - { - delete[] Frustum[i]; - } - delete[] Frustum; -} - -// old -//void FEDirectionalLight::updateCascades(float cameraFov, float aspectRatio, float nearPlane, float farPlane, glm::mat4 viewMatrix, glm::vec3 cameraForward, glm::vec3 cameraRight, glm::vec3 cameraUp) -//{ -// static glm::vec4 basisX = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f); -// static glm::vec4 basisY = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f); -// static glm::vec4 basisZ = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f); -// -// glm::vec4 fbasisX = glm::normalize(glm::toMat4(transform.getQuaternion()) * basisX); -// glm::vec4 fbasisY = glm::normalize(glm::toMat4(transform.getQuaternion()) * basisY); -// glm::vec4 fbasisZ = glm::normalize(glm::toMat4(transform.getQuaternion()) * basisZ); -// -// glm::mat4 cascadeView = glm::mat4(1.0f); -// -// cascadeView[0][0] = fbasisX.x; -// cascadeView[1][0] = fbasisX.y; -// cascadeView[2][0] = fbasisX.z; -// cascadeView[0][1] = fbasisY.x; -// cascadeView[1][1] = fbasisY.y; -// cascadeView[2][1] = fbasisY.z; -// cascadeView[0][2] = fbasisZ.x; -// cascadeView[1][2] = fbasisZ.y; -// cascadeView[2][2] = fbasisZ.z; -// -// farPlane = nearPlane; -// glm::mat4 inverseVM = glm::inverse(viewMatrix); -// static std::vector frustumEdges; -// frustumEdges.resize(8); -// -// float TEST_CASCADE_DISTANCE_SCALE_FACTOR = 1.8f; -// -// for (size_t i = 0; i < 4; i++) -// { -// cascadeData[i].viewMat = cascadeView; -// -// nearPlane = farPlane; -// farPlane = shadowCoverage * (0.0447f * float(pow(2.1867f, (i + 1)))); -// cascadeData[i].size = float(int(farPlane) - 1) * TEST_CASCADE_DISTANCE_SCALE_FACTOR; -// if (cascadeData[i].size <= 0.01f) -// cascadeData[i].size = 1.0; -// -// float firstCascadeY1 = nearPlane * tan(glm::radians(cameraFov / 2.0f)); -// float firstCascadeY2 = farPlane * tan(glm::radians(cameraFov / 2.0f)); -// -// float firstCascadeX1 = nearPlane * tan((aspectRatio) / 2.0f); -// float firstCascadeX2 = farPlane * tan((aspectRatio) / 2.0f); -// -// frustumEdges[0] = glm::vec4(firstCascadeX1, -firstCascadeY1, -nearPlane, 1.0f); -// frustumEdges[1] = glm::vec4(firstCascadeX1, firstCascadeY1, -nearPlane, 1.0f); -// frustumEdges[2] = glm::vec4(-firstCascadeX1, firstCascadeY1, -nearPlane, 1.0f); -// frustumEdges[3] = glm::vec4(-firstCascadeX1, -firstCascadeY1, -nearPlane, 1.0f); -// -// frustumEdges[4] = glm::vec4(firstCascadeX2, -firstCascadeY2, -farPlane, 1.0f); -// frustumEdges[5] = glm::vec4(firstCascadeX2, firstCascadeY2, -farPlane, 1.0f); -// frustumEdges[6] = glm::vec4(-firstCascadeX2, firstCascadeY2, -farPlane, 1.0f); -// frustumEdges[7] = glm::vec4(-firstCascadeX2, -firstCascadeY2, -farPlane, 1.0f); -// -// for (size_t j = 0; j < frustumEdges.size(); j++) -// frustumEdges[j] = cascadeData[0].viewMat * inverseVM * frustumEdges[j]; -// -// for (size_t j = 0; j < frustumEdges.size(); j++) -// frustumEdges[j].z = -frustumEdges[j].z; -// -// float minX = FLT_MAX; -// float maxX = FLT_MIN; -// float minY = FLT_MAX; -// float maxY = FLT_MIN; -// float minZ = FLT_MAX; -// float maxZ = FLT_MIN; -// -// for (size_t j = 0; j < frustumEdges.size(); j++) -// { -// minX = std::min(minX, frustumEdges[j].x); -// minY = std::min(minY, frustumEdges[j].y); -// minZ = std::min(minZ, frustumEdges[j].z); -// -// maxX = std::max(maxX, frustumEdges[j].x); -// maxY = std::max(maxY, frustumEdges[j].y); -// maxZ = std::max(maxZ, frustumEdges[j].z); -// } -// -// cascadeData[i].projectionMat = glm::ortho(minX - farPlane * (CSMXYDepth / 4.0f), maxX + farPlane * (CSMXYDepth / 4.0f), -// minY - farPlane * (CSMXYDepth / 4.0f), maxY + farPlane * (CSMXYDepth / 4.0f), -// minZ - farPlane * CSMZDepth, maxZ + farPlane * CSMZDepth); -// } -//} - - -void FEDirectionalLight::UpdateCascades(float CameraFov, float AspectRatio, float NearPlane, float FarPlane, glm::mat4 ViewMatrix, glm::vec3 CameraForward, glm::vec3 CameraRight, glm::vec3 CameraUp) -{ - static glm::vec4 BasisX = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f); - static glm::vec4 BasisY = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f); - static glm::vec4 BasisZ = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f); - - glm::vec4 FbasisX = glm::normalize(glm::toMat4(Transform.GetQuaternion()) * BasisX); - glm::vec4 FbasisY = glm::normalize(glm::toMat4(Transform.GetQuaternion()) * BasisY); - glm::vec4 FbasisZ = glm::normalize(glm::toMat4(Transform.GetQuaternion()) * BasisZ); - - glm::mat4 CascadeView = glm::mat4(1.0f); - CascadeView[0][0] = FbasisX.x; - CascadeView[1][0] = FbasisX.y; - CascadeView[2][0] = FbasisX.z; - CascadeView[0][1] = FbasisY.x; - CascadeView[1][1] = FbasisY.y; - CascadeView[2][1] = FbasisY.z; - CascadeView[0][2] = FbasisZ.x; - CascadeView[1][2] = FbasisZ.y; - CascadeView[2][2] = FbasisZ.z; - - FarPlane = NearPlane; - glm::mat4 InverseVm = glm::inverse(ViewMatrix); - static std::vector FrustumEdges; - FrustumEdges.resize(8); - - float TestCascadeDistanceScaleFactor = 4.0f; - float TestCSM0 = 0.050f; - float TestCSM1 = 0.150f; - float TestCSM2 = 0.550f; - float TestCSM3 = 1.1f; - float TestCSMScale = 1.75f; - - float TestCSMType = 1; - - // old and incorrect - if (TestCSMType == 0) - { - for (size_t i = 0; i < 4; i++) - { - CascadeData[i].ViewMat = CascadeView; - - NearPlane = FarPlane; - FarPlane = ShadowCoverage * (0.0447f * static_cast(pow(2.1867f, (i + 1)))); - CascadeData[i].Size = static_cast(static_cast(FarPlane) - 1) * TestCascadeDistanceScaleFactor; - if (CascadeData[i].Size <= 0.01f) - CascadeData[i].Size = 1.0; - - float FirstCascadeY1 = NearPlane * tan(glm::radians(CameraFov / 2.0f)); - float FirstCascadeY2 = FarPlane * tan(glm::radians(CameraFov / 2.0f)); - - float FirstCascadeX1 = NearPlane * tan((AspectRatio) / 2.0f); - float FirstCascadeX2 = FarPlane * tan((AspectRatio) / 2.0f); - - FrustumEdges[0] = glm::vec4(FirstCascadeX1, -FirstCascadeY1, -NearPlane, 1.0f); - FrustumEdges[1] = glm::vec4(FirstCascadeX1, FirstCascadeY1, -NearPlane, 1.0f); - FrustumEdges[2] = glm::vec4(-FirstCascadeX1, FirstCascadeY1, -NearPlane, 1.0f); - FrustumEdges[3] = glm::vec4(-FirstCascadeX1, -FirstCascadeY1, -NearPlane, 1.0f); - - FrustumEdges[4] = glm::vec4(FirstCascadeX2, -FirstCascadeY2, -FarPlane, 1.0f); - FrustumEdges[5] = glm::vec4(FirstCascadeX2, FirstCascadeY2, -FarPlane, 1.0f); - FrustumEdges[6] = glm::vec4(-FirstCascadeX2, FirstCascadeY2, -FarPlane, 1.0f); - FrustumEdges[7] = glm::vec4(-FirstCascadeX2, -FirstCascadeY2, -FarPlane, 1.0f); - - for (size_t j = 0; j < FrustumEdges.size(); j++) - FrustumEdges[j] = CascadeData[0].ViewMat * InverseVm * FrustumEdges[j]; - - for (size_t j = 0; j < FrustumEdges.size(); j++) - FrustumEdges[j].z = -FrustumEdges[j].z; - - float MinX = FLT_MAX; - float MaxX = FLT_MIN; - float MinY = FLT_MAX; - float MaxY = FLT_MIN; - float MinZ = FLT_MAX; - float MaxZ = FLT_MIN; - - for (size_t j = 0; j < FrustumEdges.size(); j++) - { - MinX = std::min(MinX, FrustumEdges[j].x); - MinY = std::min(MinY, FrustumEdges[j].y); - MinZ = std::min(MinZ, FrustumEdges[j].z); - - MaxX = std::max(MaxX, FrustumEdges[j].x); - MaxY = std::max(MaxY, FrustumEdges[j].y); - MaxZ = std::max(MaxZ, FrustumEdges[j].z); - } - - CascadeData[i].ProjectionMat = glm::ortho(MinX - FarPlane * (CSMXYDepth / 4.0f), MaxX + FarPlane * (CSMXYDepth / 4.0f), - MinY - FarPlane * (CSMXYDepth / 4.0f), MaxY + FarPlane * (CSMXYDepth / 4.0f), - MinZ - FarPlane * CSMZDepth, MaxZ + FarPlane * CSMZDepth); - } - } - else if (TestCSMType == 1) - { - for (size_t i = 0; i < 4; i++) - { - CascadeData[i].ViewMat = CascadeView; - - NearPlane = FarPlane; - if (i == 0) - { - FarPlane = (ShadowCoverage / 4.0f) * TestCSM0; - } - else if (i == 1) - { - FarPlane = (ShadowCoverage / 4.0f) * TestCSM1; - } - else if (i == 2) - { - FarPlane = (ShadowCoverage / 4.0f) * TestCSM2; - } - else if (i == 3) - { - FarPlane = (ShadowCoverage / 4.0f) * TestCSM3; - } - CascadeData[i].Size = FarPlane * TestCSMScale; - - float FirstCascadeY1 = NearPlane * tan(glm::radians(CameraFov / 2.0f)); - float FirstCascadeY2 = FarPlane * tan(glm::radians(CameraFov / 2.0f)); - - float FirstCascadeX1 = NearPlane * tan((AspectRatio) / 2.0f); - float FirstCascadeX2 = FarPlane * tan((AspectRatio) / 2.0f); - - FrustumEdges[0] = glm::vec4(FirstCascadeX1, -FirstCascadeY1, -NearPlane, 1.0f); - FrustumEdges[1] = glm::vec4(FirstCascadeX1, FirstCascadeY1, -NearPlane, 1.0f); - FrustumEdges[2] = glm::vec4(-FirstCascadeX1, FirstCascadeY1, -NearPlane, 1.0f); - FrustumEdges[3] = glm::vec4(-FirstCascadeX1, -FirstCascadeY1, -NearPlane, 1.0f); - - FrustumEdges[4] = glm::vec4(FirstCascadeX2, -FirstCascadeY2, -FarPlane, 1.0f); - FrustumEdges[5] = glm::vec4(FirstCascadeX2, FirstCascadeY2, -FarPlane, 1.0f); - FrustumEdges[6] = glm::vec4(-FirstCascadeX2, FirstCascadeY2, -FarPlane, 1.0f); - FrustumEdges[7] = glm::vec4(-FirstCascadeX2, -FirstCascadeY2, -FarPlane, 1.0f); - - for (size_t j = 0; j < FrustumEdges.size(); j++) - FrustumEdges[j] = CascadeData[0].ViewMat * InverseVm * FrustumEdges[j]; - - for (size_t j = 0; j < FrustumEdges.size(); j++) - FrustumEdges[j].z = -FrustumEdges[j].z; - - float MinX = FLT_MAX; - float MaxX = -FLT_MAX; - float MinY = FLT_MAX; - float MaxY = -FLT_MAX; - float MinZ = FLT_MAX; - float MaxZ = -FLT_MAX; - - for (size_t j = 0; j < FrustumEdges.size(); j++) - { - MinX = std::min(MinX, FrustumEdges[j].x); - MinY = std::min(MinY, FrustumEdges[j].y); - MinZ = std::min(MinZ, FrustumEdges[j].z); - - MaxX = std::max(MaxX, FrustumEdges[j].x); - MaxY = std::max(MaxY, FrustumEdges[j].y); - MaxZ = std::max(MaxZ, FrustumEdges[j].z); - } - - float left = MinX - FarPlane * (CSMXYDepth / 4.0f); - float right = MaxX + FarPlane * (CSMXYDepth / 4.0f); - - float bottom = MinY - FarPlane * (CSMXYDepth / 4.0f); - float top = MaxY + FarPlane * (CSMXYDepth / 4.0f); - - float ZNear = MinZ - FarPlane * CSMZDepth; - float ZFar = MaxZ + FarPlane * CSMZDepth; - - CascadeData[i].ProjectionMat = glm::ortho(left, right, - bottom, top, - ZNear, ZFar); - - float clip[16]; - float t; - - glm::mat4 cliping = CascadeData[i].ProjectionMat * CascadeView; - for (int j = 0; j < 4; j++) - { - clip[j * 4] = cliping[j][0]; - clip[j * 4 + 1] = cliping[j][1]; - clip[j * 4 + 2] = cliping[j][2]; - clip[j * 4 + 3] = cliping[j][3]; - } - - /* Extract the numbers for the RIGHT plane */ - CascadeData[i].Frustum[0][0] = clip[3] - clip[0]; - CascadeData[i].Frustum[0][1] = clip[7] - clip[4]; - CascadeData[i].Frustum[0][2] = clip[11] - clip[8]; - CascadeData[i].Frustum[0][3] = clip[15] - clip[12]; - - /* Normalize the result */ - t = sqrt(CascadeData[i].Frustum[0][0] * CascadeData[i].Frustum[0][0] + CascadeData[i].Frustum[0][1] * CascadeData[i].Frustum[0][1] + CascadeData[i].Frustum[0][2] * CascadeData[i].Frustum[0][2]); - CascadeData[i].Frustum[0][0] /= t; - CascadeData[i].Frustum[0][1] /= t; - CascadeData[i].Frustum[0][2] /= t; - CascadeData[i].Frustum[0][3] /= t; - - /* Extract the numbers for the LEFT plane */ - CascadeData[i].Frustum[1][0] = clip[3] + clip[0]; - CascadeData[i].Frustum[1][1] = clip[7] + clip[4]; - CascadeData[i].Frustum[1][2] = clip[11] + clip[8]; - CascadeData[i].Frustum[1][3] = clip[15] + clip[12]; - - /* Normalize the result */ - t = sqrt(CascadeData[i].Frustum[1][0] * CascadeData[i].Frustum[1][0] + CascadeData[i].Frustum[1][1] * CascadeData[i].Frustum[1][1] + CascadeData[i].Frustum[1][2] * CascadeData[i].Frustum[1][2]); - CascadeData[i].Frustum[1][0] /= t; - CascadeData[i].Frustum[1][1] /= t; - CascadeData[i].Frustum[1][2] /= t; - CascadeData[i].Frustum[1][3] /= t; - - /* Extract the BOTTOM plane */ - CascadeData[i].Frustum[2][0] = clip[3] + clip[1]; - CascadeData[i].Frustum[2][1] = clip[7] + clip[5]; - CascadeData[i].Frustum[2][2] = clip[11] + clip[9]; - CascadeData[i].Frustum[2][3] = clip[15] + clip[13]; - - /* Normalize the result */ - t = sqrt(CascadeData[i].Frustum[2][0] * CascadeData[i].Frustum[2][0] + CascadeData[i].Frustum[2][1] * CascadeData[i].Frustum[2][1] + CascadeData[i].Frustum[2][2] * CascadeData[i].Frustum[2][2]); - CascadeData[i].Frustum[2][0] /= t; - CascadeData[i].Frustum[2][1] /= t; - CascadeData[i].Frustum[2][2] /= t; - CascadeData[i].Frustum[2][3] /= t; - - /* Extract the TOP plane */ - CascadeData[i].Frustum[3][0] = clip[3] - clip[1]; - CascadeData[i].Frustum[3][1] = clip[7] - clip[5]; - CascadeData[i].Frustum[3][2] = clip[11] - clip[9]; - CascadeData[i].Frustum[3][3] = clip[15] - clip[13]; - - /* Normalize the result */ - t = sqrt(CascadeData[i].Frustum[3][0] * CascadeData[i].Frustum[3][0] + CascadeData[i].Frustum[3][1] * CascadeData[i].Frustum[3][1] + CascadeData[i].Frustum[3][2] * CascadeData[i].Frustum[3][2]); - CascadeData[i].Frustum[3][0] /= t; - CascadeData[i].Frustum[3][1] /= t; - CascadeData[i].Frustum[3][2] /= t; - CascadeData[i].Frustum[3][3] /= t; - - /* Extract the FAR plane */ - CascadeData[i].Frustum[4][0] = clip[3] - clip[2]; - CascadeData[i].Frustum[4][1] = clip[7] - clip[6]; - CascadeData[i].Frustum[4][2] = clip[11] - clip[10]; - CascadeData[i].Frustum[4][3] = clip[15] - clip[14]; - - /* Normalize the result */ - t = sqrt(CascadeData[i].Frustum[4][0] * CascadeData[i].Frustum[4][0] + CascadeData[i].Frustum[4][1] * CascadeData[i].Frustum[4][1] + CascadeData[i].Frustum[4][2] * CascadeData[i].Frustum[4][2]); - CascadeData[i].Frustum[4][0] /= t; - CascadeData[i].Frustum[4][1] /= t; - CascadeData[i].Frustum[4][2] /= t; - CascadeData[i].Frustum[4][3] /= t; - - /* Extract the NEAR plane */ - CascadeData[i].Frustum[5][0] = clip[3] + clip[2]; - CascadeData[i].Frustum[5][1] = clip[7] + clip[6]; - CascadeData[i].Frustum[5][2] = clip[11] + clip[10]; - CascadeData[i].Frustum[5][3] = clip[15] + clip[14]; - - /* Normalize the result */ - t = sqrt(CascadeData[i].Frustum[5][0] * CascadeData[i].Frustum[5][0] + CascadeData[i].Frustum[5][1] * CascadeData[i].Frustum[5][1] + CascadeData[i].Frustum[5][2] * CascadeData[i].Frustum[5][2]); - CascadeData[i].Frustum[5][0] /= t; - CascadeData[i].Frustum[5][1] /= t; - CascadeData[i].Frustum[5][2] /= t; - CascadeData[i].Frustum[5][3] /= t; - - int ty = 0; - ty++; - } - } - // my way of frustum reconstruction - else if (TestCSMType == 2) - { - float NearPlaneXLength = NearPlane * glm::tan(AspectRatio / 2.0f) * 2.0f; - float NearPlaneYLength = NearPlaneXLength / AspectRatio; - - glm::vec3 CameraPos = glm::vec3(ViewMatrix[3][0], ViewMatrix[3][1], ViewMatrix[3][2]); - - glm::vec3 NearTopLeft = CameraPos; - NearTopLeft += -CameraRight * (NearPlaneXLength / 2.0f); - NearTopLeft += CameraUp * (NearPlaneYLength / 2.0f); - NearTopLeft += CameraForward * NearPlane; - - glm::vec3 NearTopRight = CameraPos; - NearTopRight += CameraRight * (NearPlaneXLength / 2.0f); - NearTopRight += CameraUp * (NearPlaneYLength / 2.0f); - NearTopRight += CameraForward * NearPlane; - - glm::vec3 NearBottomLeft = CameraPos; - NearBottomLeft += -CameraRight * (NearPlaneXLength / 2.0f); - NearBottomLeft += -CameraUp * (NearPlaneYLength / 2.0f); - NearBottomLeft += CameraForward * NearPlane; - - glm::vec3 NearBottomRight = CameraPos; - NearBottomRight += CameraRight * (NearPlaneXLength / 2.0f); - NearBottomRight += -CameraUp * (NearPlaneYLength / 2.0f); - NearBottomRight += CameraForward * NearPlane; - - glm::vec3 CameraToTopLeft = glm::normalize(NearTopLeft - CameraPos); - glm::vec3 CameraToTopRight = glm::normalize(NearTopRight - CameraPos); - glm::vec3 CameraToBottomLeft = glm::normalize(NearBottomLeft - CameraPos); - glm::vec3 CameraToBottomRight = glm::normalize(NearBottomRight - CameraPos); - - for (size_t i = 0; i < 4; i++) - { - CascadeData[i].ViewMat = CascadeView; - - NearPlane = FarPlane; - if (i == 0) - { - FarPlane = (ShadowCoverage / 4.0f) * TestCSM0; - } - else if (i == 1) - { - FarPlane = (ShadowCoverage / 4.0f) * TestCSM1; - } - else if (i == 2) - { - FarPlane = (ShadowCoverage / 4.0f) * TestCSM2; - } - else if (i == 3) - { - FarPlane = (ShadowCoverage / 4.0f) * TestCSM3; - } - CascadeData[i].Size = FarPlane * TestCSMScale; - - CameraPos = glm::vec3(0.0f); - - FrustumEdges[0] = glm::vec4(CameraPos + CameraToTopLeft * NearPlane, 1.0f); - FrustumEdges[1] = glm::vec4(CameraPos + CameraToTopRight * NearPlane, 1.0f); - FrustumEdges[2] = glm::vec4(CameraPos + CameraToBottomLeft * NearPlane, 1.0f); - FrustumEdges[3] = glm::vec4(CameraPos + CameraToBottomRight * NearPlane, 1.0f); - - FrustumEdges[4] = glm::vec4(CameraPos + CameraToTopLeft * FarPlane, 1.0f); - FrustumEdges[5] = glm::vec4(CameraPos + CameraToTopRight * FarPlane, 1.0f); - FrustumEdges[6] = glm::vec4(CameraPos + CameraToBottomLeft * FarPlane, 1.0f); - FrustumEdges[7] = glm::vec4(CameraPos + CameraToBottomRight * FarPlane, 1.0f); - - for (size_t j = 0; j < FrustumEdges.size(); j++) - FrustumEdges[j] = CascadeData[0].ViewMat * InverseVm * FrustumEdges[j]; - - for (size_t j = 0; j < FrustumEdges.size(); j++) - FrustumEdges[j].z = -FrustumEdges[j].z; - - float MinX = FLT_MAX; - float MaxX = -FLT_MAX; - float MinY = FLT_MAX; - float MaxY = -FLT_MAX; - float MinZ = FLT_MAX; - float MaxZ = -FLT_MAX; - - for (size_t j = 0; j < FrustumEdges.size(); j++) - { - MinX = std::min(MinX, FrustumEdges[j].x); - MinY = std::min(MinY, FrustumEdges[j].y); - MinZ = std::min(MinZ, FrustumEdges[j].z); - - MaxX = std::max(MaxX, FrustumEdges[j].x); - MaxY = std::max(MaxY, FrustumEdges[j].y); - MaxZ = std::max(MaxZ, FrustumEdges[j].z); - } - - CascadeData[i].ProjectionMat = glm::ortho(MinX - FarPlane * (CSMXYDepth / 4.0f), MaxX + FarPlane * (CSMXYDepth / 4.0f), - MinY - FarPlane * (CSMXYDepth / 4.0f), MaxY + FarPlane * (CSMXYDepth / 4.0f), - MinZ - FarPlane * CSMZDepth, MaxZ + FarPlane * CSMZDepth); - } - } -} - -FEDirectionalLight::FEDirectionalLight() : FELight(FE_DIRECTIONAL_LIGHT) -{ -} - -glm::vec3 FEDirectionalLight::GetDirection() -{ - Direction = glm::normalize(Transform.GetTransformMatrix() * glm::vec4(DefaultDirection, 0.0f)); - return Direction; -} - -void FEDirectionalLight::SetDirection(const glm::vec3 NewValue) -{ - Direction = NewValue; -} - -FEDirectionalLight::~FEDirectionalLight() -{ - delete CascadeData[0].FrameBuffer; - delete CascadeData[1].FrameBuffer; - delete CascadeData[2].FrameBuffer; - delete CascadeData[3].FrameBuffer; -} - -int FEDirectionalLight::GetActiveCascades() -{ - return ActiveCascades; -} - -void FEDirectionalLight::SetActiveCascades(int NewValue) -{ - if (NewValue < 1 || NewValue > 4) - NewValue = 1; - - ActiveCascades = NewValue; -} - -float FEDirectionalLight::GetShadowCoverage() -{ - return ShadowCoverage; -} - -void FEDirectionalLight::SetShadowCoverage(float NewValue) -{ - if (NewValue <= 0.0f) - NewValue = 0.1f; - - ShadowCoverage = NewValue; - //updateProjectionMat(); -} - -float FEDirectionalLight::GetCSMZDepth() -{ - return CSMZDepth; -} - -void FEDirectionalLight::SetCSMZDepth(float NewValue) -{ - if (NewValue <= 0.5f) - NewValue = 0.5f; - - CSMZDepth = NewValue; -} - -float FEDirectionalLight::GetCSMXYDepth() -{ - return CSMXYDepth; -} - -void FEDirectionalLight::SetCSMXYDepth(float NewValue) -{ - if (NewValue <= 0.5f) - NewValue = 0.5f; - - CSMXYDepth = NewValue; -} - -void FEDirectionalLight::SetCastShadows(const bool NewValue) -{ - FELight::SetCastShadows(NewValue); - if (!NewValue) - { - for (size_t i = 0; i < static_cast(ActiveCascades); i++) - { - CascadeData[i].FrameBuffer->Bind(); - FE_GL_ERROR(glClear(GL_DEPTH_BUFFER_BIT)); - CascadeData[i].FrameBuffer->UnBind(); - } - } -} - -FESpotLight::FESpotLight() : FELight(FE_SPOT_LIGHT) -{ -} - -FESpotLight::~FESpotLight() -{ -} - -float FESpotLight::GetSpotAngle() -{ - return SpotAngle; -} - -void FESpotLight::SetSpotAngle(const float NewValue) -{ - SpotAngle = NewValue; -} - -float FESpotLight::GetSpotAngleOuter() -{ - return SpotAngleOuter; -} - -void FESpotLight::SetSpotAngleOuter(const float NewValue) -{ - SpotAngleOuter = NewValue; -} - -float FESpotLight::GetRange() -{ - return Range; -} - -void FESpotLight::SetRange(const float NewValue) -{ - Range = NewValue; -} - -glm::vec3 FESpotLight::GetDirection() -{ - Direction = glm::normalize(Transform.GetTransformMatrix() * glm::vec4(DefaultDirection, 0.0f)); - return Direction; -} - -void FESpotLight::SetDirection(const glm::vec3 NewValue) -{ - Direction = NewValue; -} - -FEPointLight::FEPointLight() : FELight(FE_POINT_LIGHT) -{ -} - -FEPointLight::~FEPointLight() -{ -} - -float FEPointLight::GetRange() -{ - return Range; -} - -void FEPointLight::SetRange(const float NewValue) -{ - Range = NewValue; -} - -bool FELight::IsStaticShadowBias() -{ - return bStaticShadowBias; -} - -void FELight::SetIsStaticShadowBias(const bool NewValue) -{ - bStaticShadowBias = NewValue; -} - -float FELight::GetShadowBiasVariableIntensity() -{ - return ShadowBiasVariableIntensity; -} - -void FELight::SetShadowBiasVariableIntensity(float NewValue) -{ - if (NewValue <= 0.0f) - NewValue = 0.01f; - - ShadowBiasVariableIntensity = NewValue; -} \ No newline at end of file diff --git a/Renderer/FELine.h b/Renderer/FELine.h index e92f814..abd64bb 100644 --- a/Renderer/FELine.h +++ b/Renderer/FELine.h @@ -3,7 +3,7 @@ #ifndef FELINE_H #define FELINE_H -#include "../SubSystems/Scene/FETransformComponent.h" +#include "../Core/FEObject.h" namespace FocalEngine { diff --git a/Renderer/FEMaterial.cpp b/Renderer/FEMaterial.cpp index ac0b6bb..feb6651 100644 --- a/Renderer/FEMaterial.cpp +++ b/Renderer/FEMaterial.cpp @@ -37,8 +37,20 @@ void FEMaterial::Bind() Textures[i]->Bind(static_cast(i)); } - // #fix such specific if statement in this class is not clean coding - Shader->UpdateParameterData("baseColor", BaseColor); + auto UniformVariationIterator = UniformVariations.begin(); + while (UniformVariationIterator != UniformVariations.end()) + { + FEShaderUniform* Uniform = Shader->GetUniform(UniformVariationIterator->first); + if (Uniform == nullptr) + { + LOG.Add("FEMaterial::Bind() failed to bind variation on non existing uniform: " + UniformVariationIterator->first, "FE_LOG_RENDERING", FE_LOG_WARNING); + UniformVariationIterator++; + continue; + } + + Uniform->CurrentValue = UniformVariationIterator->second; + UniformVariationIterator++; + } } void FEMaterial::UnBind() @@ -53,60 +65,62 @@ void FEMaterial::UnBind() } } -void FEMaterial::SetParam(const std::string Name, const int NewData) const -{ - Shader->UpdateParameterData(Name, NewData); -} - -void FEMaterial::SetParam(const std::string Name, const float NewData) const -{ - Shader->UpdateParameterData(Name, NewData); -} - -void FEMaterial::SetParam(const std::string Name, const glm::vec2 NewData) const +bool FEMaterial::SetUniformVariation(const FEShaderUniformValue NewUniformVariation) { - Shader->UpdateParameterData(Name, NewData); -} + if (Shader->GetUniform(NewUniformVariation.GetName()) == nullptr) + { + LOG.Add("FEMaterial::SetUniformVariation() failed to set variation on non existing uniform", "FE_LOG_RENDERING", FE_LOG_WARNING); + return false; + } -void FEMaterial::SetParam(const std::string Name, const glm::vec3 NewData) const -{ - Shader->UpdateParameterData(Name, NewData); + UniformVariations[NewUniformVariation.GetName()] = NewUniformVariation; + return true; } -void FEMaterial::SetParam(const std::string Name, const glm::vec4 NewData) const +std::vector FEMaterial::GetUniformNameList() const { - Shader->UpdateParameterData(Name, NewData); + return Shader->GetUniformNameList(); } -void FEMaterial::SetParam(const std::string Name, const glm::mat4 NewData) const +std::vector FEMaterial::GetUniformVariationsNameList() const { - Shader->UpdateParameterData(Name, NewData); + FE_MAP_TO_STR_VECTOR(UniformVariations); } -void FEMaterial::AddParameter(const FEShaderParam NewParameter) const +FEShaderUniformValue* FEMaterial::GetUniformVariation(std::string Name) { - Shader->AddParameter(NewParameter); -} + if (UniformVariations.find(Name) == UniformVariations.end()) + return nullptr; -std::vector FEMaterial::GetParameterList() const -{ - return Shader->GetParameterList(); + return &UniformVariations[Name]; } -FEShaderParam* FEMaterial::GetParameter(const std::string Name) const +glm::vec3 FEMaterial::GetBaseColor() { - return Shader->GetParameter(Name); -} + if (Shader->GetUniform("baseColor") == nullptr) + { + LOG.Add("FEMaterial::GetBaseColor() failed to set base color on non existing uniform", "FE_LOG_RENDERING", FE_LOG_WARNING); + return glm::vec3(); + } -glm::vec3 FEMaterial::GetBaseColor() const -{ - return BaseColor; + if (UniformVariations.find("baseColor") == UniformVariations.end()) + { + LOG.Add("FEMaterial::GetBaseColor() failed to set base color on non existing uniform", "FE_LOG_RENDERING", FE_LOG_WARNING); + return glm::vec3(); + } + + return UniformVariations["baseColor"].GetValue(); } -// Only influence color of object if shader with such uniform is applied. void FEMaterial::SetBaseColor(const glm::vec3 NewValue) { - BaseColor = NewValue; + if (Shader->GetUniform("baseColor") == nullptr) + { + LOG.Add("FEMaterial::SetBaseColor() failed to set base color on non existing uniform", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + UniformVariations["baseColor"] = FEShaderUniformValue("baseColor", NewValue); } float FEMaterial::GetMetalness() const @@ -419,7 +433,7 @@ FETexture* FEMaterial::GetSpecifiedMap(const int BindingIndex, const int SubMate if (TextureBindings[BindingIndex + SubMaterial * 6] == -1) return nullptr; - // clean up messed up textureBindings. + // Clean up invalid TextureBindings if (Textures[TextureBindings[BindingIndex + SubMaterial * 6]] == nullptr) { TextureBindings[BindingIndex + SubMaterial * 6] = -1; @@ -496,7 +510,7 @@ void FEMaterial::ClearAllTexturesInfo() bool FEMaterial::IsTextureInList(const FETexture* Texture) const { - bool result = false; + bool Result = false; for (size_t i = 0; i < FE_MAX_TEXTURES_PER_MATERIAL; i++) { if (Textures[i] == nullptr) @@ -504,12 +518,12 @@ bool FEMaterial::IsTextureInList(const FETexture* Texture) const if (Textures[i]->GetObjectID() == Texture->GetObjectID()) { - result = true; - return result; + Result = true; + return Result; } } - return result; + return Result; } bool FEMaterial::IsCompackPacking() @@ -593,14 +607,14 @@ void FEMaterial::ClearTextureBinding(const int Index, const int SubMaterial, con int FEMaterial::GetUsedTexturesCount() const { - int result = 0; + int Result = 0; for (size_t i = 0; i < Textures.size(); i++) { if (Textures[i] != nullptr) - result++; + Result++; } - return result; + return Result; } float FEMaterial::GetDisplacementMapIntensity() const diff --git a/Renderer/FEMaterial.h b/Renderer/FEMaterial.h index 82b01fd..e57959b 100644 --- a/Renderer/FEMaterial.h +++ b/Renderer/FEMaterial.h @@ -18,28 +18,22 @@ namespace FocalEngine FEMaterial(std::string Name); ~FEMaterial(); - FEShader* Shader; + FEShader* Shader = nullptr; - virtual void Bind(); - virtual void UnBind(); - - void SetParam(std::string Name, int NewData) const; - void SetParam(std::string Name, float NewData) const; - void SetParam(std::string Name, glm::vec2 NewData) const; - void SetParam(std::string Name, glm::vec3 NewData) const; - void SetParam(std::string Name, glm::vec4 NewData) const; - void SetParam(std::string Name, glm::mat4 NewData) const; + void Bind(); + void UnBind(); std::vector Textures; std::vector TextureBindings; std::vector TextureChannels; - void AddParameter(FEShaderParam NewParameter) const; - std::vector GetParameterList() const; - FEShaderParam* GetParameter(std::string Name) const; + std::vector GetUniformNameList() const; + std::vector GetUniformVariationsNameList() const; + + bool SetUniformVariation(FEShaderUniformValue NewUniformVariation); + FEShaderUniformValue* GetUniformVariation(std::string Name); - glm::vec3 GetBaseColor() const; - // Only influence color of object if shader with such uniform is applied. + glm::vec3 GetBaseColor(); void SetBaseColor(glm::vec3 NewValue); float GetMetalness() const; @@ -109,8 +103,7 @@ namespace FocalEngine float GetTiling() const; void SetTiling(float NewValue); private: - glm::vec3 DiffuseColor; - glm::vec3 BaseColor; + std::unordered_map UniformVariations; int PlaceTextureInList(FETexture* Texture); void SetTextureBinding(int Index, int TextureIndex, int SubMaterial, int Channel = -2); diff --git a/Renderer/FEMesh.h b/Renderer/FEMesh.h index a2b50a0..ee2caca 100644 --- a/Renderer/FEMesh.h +++ b/Renderer/FEMesh.h @@ -5,18 +5,14 @@ namespace FocalEngine { - class FEEntity; - class FEEntityInstanced; - class FERenderer; - class FEResourceManager; - class FEMesh : public FEObject { - friend FEEntity; - friend FEEntityInstanced; - friend FERenderer; - friend FEResourceManager; + friend class FEEntity; + friend class FEInstancedSystem; + friend class FERenderer; + friend class FEResourceManager; public: + FEMesh() : FEObject(FE_MESH, "Name") {}; FEMesh(GLuint VaoID, unsigned int VertexCount, int VertexBuffersTypes, FEAABB AABB, std::string Name); ~FEMesh(); diff --git a/Renderer/FEPostProcess.cpp b/Renderer/FEPostProcess.cpp index bc0a050..d8e3f37 100644 --- a/Renderer/FEPostProcess.cpp +++ b/Renderer/FEPostProcess.cpp @@ -1,6 +1,9 @@ #include "FEPostProcess.h" using namespace FocalEngine; +FEMesh* FEPostProcess::ScreenQuad = nullptr; +FEShader* FEPostProcess::ScreenQuadShader = nullptr; + FEPostProcessStage::FEPostProcessStage(const int InTextureSource, FEShader* Shader) { this->InTextureSource.push_back(InTextureSource); @@ -27,10 +30,10 @@ FEPostProcess::~FEPostProcess() delete IntermediateFramebuffer; } -void FocalEngine::FEPostProcess::RenderResult() +void FEPostProcess::RenderResult() { ScreenQuadShader->Start(); - ScreenQuadShader->LoadDataToGPU(); + ScreenQuadShader->LoadUniformsDataToGPU(); Stages.back()->OutTexture->Bind(0); FE_GL_ERROR(glBindVertexArray(ScreenQuad->GetVaoID())); diff --git a/Renderer/FEPostProcess.h b/Renderer/FEPostProcess.h index 6f1aacd..2b84b05 100644 --- a/Renderer/FEPostProcess.h +++ b/Renderer/FEPostProcess.h @@ -1,6 +1,7 @@ #pragma once -#include "FEEntity.h" +#include "FEFramebuffer.h" +#include "FEMesh.h" namespace FocalEngine { @@ -17,7 +18,7 @@ namespace FocalEngine FEPostProcessStage(int InTextureSource, FEShader* Shader); FEPostProcessStage(std::vector&& InTextureSource, FEShader* Shader); - std::vector StageSpecificUniforms; + std::vector StageSpecificUniformValues; std::vector InTextureSource; std::vector InTexture; FEShader* Shader = nullptr; @@ -45,8 +46,8 @@ namespace FocalEngine FEPostProcess(std::string Name); int ScreenWidth, ScreenHeight; - FEMesh* ScreenQuad; - FEShader* ScreenQuadShader; + static FEMesh* ScreenQuad; + static FEShader* ScreenQuadShader; FETexture* InTexture = nullptr; FETexture* FinalTexture = nullptr; diff --git a/Renderer/FEPrefab.cpp b/Renderer/FEPrefab.cpp deleted file mode 100644 index f4c3bda..0000000 --- a/Renderer/FEPrefab.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "FEPrefab.h" -using namespace FocalEngine; - -FEPrefab::FEPrefab() : FEObject(FE_PREFAB, "") -{ - SetDirtyFlag(true); -} - -FEPrefab::FEPrefab(FEGameModel* GameModel, const std::string Name) : FEObject(FE_PREFAB, Name) -{ - Components.push_back(new FEPrefabComponent()); - Components.back()->GameModel = GameModel; - - SetDirtyFlag(true); -} - -FEPrefab::~FEPrefab() -{ - for (int i = 0; i < Components.size(); i++) - { - delete Components[i]; - } -} - -void FEPrefab::UpdateAABB() -{ - AABB = FEAABB(); - - for (int i = 0; i < Components.size(); i++) - { - if (Components[i]->GameModel == nullptr || Components[i]->GameModel->Mesh == nullptr) - continue; - AABB = AABB.Merge(Components[i]->GameModel->Mesh->GetAABB().Transform(Components[i]->Transform.GetTransformMatrix())); - } -} - -FEAABB FEPrefab::GetAABB() -{ - if (IsDirty()) - UpdateAABB(); - - return AABB; -} - -bool FEPrefab::UsesMaterial(const std::string MaterialID) const -{ - for (int i = 0; i < Components.size(); i++) - { - if (Components[i]->GameModel->Material->GetObjectID() == MaterialID) - return true; - - if (Components[i]->GameModel->BillboardMaterial != nullptr && Components[i]->GameModel->BillboardMaterial->GetObjectID() == MaterialID) - return true; - } - - return false; -} - -bool FEPrefab::UsesGameModel(const std::string GameModelID) const -{ - for (int i = 0; i < Components.size(); i++) - { - if (Components[i]->GameModel->GetObjectID() == GameModelID) - return true; - } - - return false; -} - -int FEPrefab::ComponentsCount() const -{ - return static_cast(Components.size()); -} - -void FEPrefab::AddComponent(FEGameModel* GameModel, const FETransformComponent Transform) -{ - if (GameModel == nullptr) - return; - - Components.push_back(new FEPrefabComponent()); - Components.back()->GameModel = GameModel; - Components.back()->Transform = Transform; - - SetDirtyFlag(true); -} - -FEPrefabComponent* FEPrefab::GetComponent(const int Index) const -{ - if (Index >= Components.size()) - return nullptr; - - return Components[Index]; -} - -void FEPrefab::RemoveComponent(const FEGameModel* GameModel) -{ - for (int i = 0; i < Components.size(); i++) - { - if (Components[i]->GameModel == GameModel) - { - RemoveComponent(i); - i--; - } - } -} - -void FEPrefab::RemoveComponent(const int Index) -{ - if (Index >= Components.size()) - return; - - delete Components[Index]; - Components.erase(Components.begin() + Index); - - SetDirtyFlag(true); -} \ No newline at end of file diff --git a/Renderer/FEPrefab.h b/Renderer/FEPrefab.h deleted file mode 100644 index 1e7e59f..0000000 --- a/Renderer/FEPrefab.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#ifndef FEPREFAB_H -#define FEPREFAB_H - -#include "../SubSystems/Scene/FETransformComponent.h" -#include "FEGameModel.h" - -namespace FocalEngine -{ - struct FEPrefabComponent - { - // Later it should be FEObject because we could use not only FEGameModel but something like FEAnimation or other classes as components. - FEGameModel* GameModel = nullptr; - FETransformComponent Transform; - }; - - class FEPrefab : public FEObject - { - friend FERenderer; - friend FEResourceManager; - friend FEEntity; - friend FEEntityInstanced; - - std::vector Components; - - FEAABB AABB; - void UpdateAABB(); - - FEPrefab(); - public: - FEPrefab(FEGameModel* GameModel, std::string Name = ""); - ~FEPrefab(); - - int ComponentsCount() const; - void AddComponent(FEGameModel* GameModel, FETransformComponent Transform = FETransformComponent()); - FEPrefabComponent* GetComponent(int Index) const; - void RemoveComponent(const FEGameModel* GameModel); - void RemoveComponent(int Index); - - FEAABB GetAABB(); - - bool UsesMaterial(std::string MaterialID) const; - bool UsesGameModel(std::string GameModelID) const; - }; -} - -#endif FEPREFAB_H \ No newline at end of file diff --git a/Renderer/FERenderer.cpp b/Renderer/FERenderer.cpp index 023dfd0..adb2d97 100644 --- a/Renderer/FERenderer.cpp +++ b/Renderer/FERenderer.cpp @@ -1,7 +1,13 @@ #include "FERenderer.h" +#include "../FEngine.h" using namespace FocalEngine; -FERenderer* FERenderer::Instance = nullptr; +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetRenderer() +{ + return FERenderer::GetInstancePointer(); +} +#endif FERenderer::FERenderer() { @@ -9,279 +15,321 @@ FERenderer::FERenderer() void FERenderer::Init() { - if (bSimplifiedRendering) - { - // Because of VR - glGenBuffers(1, &UniformBufferForLights); - glBindBuffer(GL_UNIFORM_BUFFER, UniformBufferForLights); - glBufferData(GL_UNIFORM_BUFFER, FE_MAX_LIGHTS * UBufferForLightSize, nullptr, GL_STATIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - glBindBufferRange(GL_UNIFORM_BUFFER, UniformBufferCount++, UniformBufferForLights, 0, FE_MAX_LIGHTS * UBufferForLightSize); + RENDERER.InstancedLineShader = RESOURCE_MANAGER.CreateShader("instancedLine", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//InstancedLineMaterial//FE_InstancedLine_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//InstancedLineMaterial//FE_InstancedLine_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "7E0826291010377D564F6115"); + RESOURCE_MANAGER.SetTagIternal(RENDERER.InstancedLineShader, ENGINE_RESOURCE_TAG); + + FEShader* FEScreenQuadShader = RESOURCE_MANAGER.CreateShader("FEScreenQuadShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_ScreenQuad_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_ScreenQuad_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "7933272551311F3A1A5B2363"); + + RESOURCE_MANAGER.SetTagIternal(FEScreenQuadShader, ENGINE_RESOURCE_TAG); + + glGenBuffers(1, &UniformBufferForLights); + glBindBuffer(GL_UNIFORM_BUFFER, UniformBufferForLights); + glBufferData(GL_UNIFORM_BUFFER, FE_MAX_LIGHTS * UBufferForLightSize, nullptr, GL_STATIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + glBindBufferRange(GL_UNIFORM_BUFFER, UniformBufferCount++, UniformBufferForLights, 0, FE_MAX_LIGHTS * UBufferForLightSize); + + glGenBuffers(1, &UniformBufferForDirectionalLight); + glBindBuffer(GL_UNIFORM_BUFFER, UniformBufferForDirectionalLight); + glBufferData(GL_UNIFORM_BUFFER, UBufferForDirectionalLightSize, nullptr, GL_STATIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + glBindBufferRange(GL_UNIFORM_BUFFER, UniformBufferCount++, UniformBufferForDirectionalLight, 0, UBufferForDirectionalLightSize); + + // Instanced lines + LinesBuffer.resize(FE_MAX_LINES); + + const float QuadVertices[] = { + 0.0f, -0.5f, 0.0f, + 1.0f, -0.5f, 1.0f, + 1.0f, 0.5f, 1.0f, + + 0.0f, -0.5f, 0.0f, + 1.0f, 0.5f, 1.0f, + 0.0f, 0.5f, 0.0f, + }; + glGenVertexArrays(1, &InstancedLineVAO); + glBindVertexArray(InstancedLineVAO); + + unsigned int QuadVBO; + glGenBuffers(1, &QuadVBO); + glBindBuffer(GL_ARRAY_BUFFER, QuadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(QuadVertices), QuadVertices, GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr); + + glGenBuffers(1, &InstancedLineBuffer); + glBindBuffer(GL_ARRAY_BUFFER, InstancedLineBuffer); + glBufferData(GL_ARRAY_BUFFER, LinesBuffer.size() * sizeof(FELine), LinesBuffer.data(), GL_DYNAMIC_DRAW); + + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(FELine), static_cast(nullptr)); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(FELine), (void*)(3 * sizeof(float))); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(FELine), (void*)(6 * sizeof(float))); + glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(FELine), (void*)(9 * sizeof(float))); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glVertexAttribDivisor(0, 0); + glVertexAttribDivisor(1, 1); + glVertexAttribDivisor(2, 1); + glVertexAttribDivisor(3, 1); + glVertexAttribDivisor(4, 1); + + glBindVertexArray(0); + + FrustumCullingShader = RESOURCE_MANAGER.CreateShader("FE_FrustumCullingShader", + nullptr, nullptr, + nullptr, nullptr, + nullptr, RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//ComputeShaders//FE_FrustumCulling_CS.glsl").c_str()).c_str()); + + FE_GL_ERROR(glGenBuffers(1, &FrustumInfoBuffer)); + FE_GL_ERROR(glGenBuffers(1, &CullingLODCountersBuffer)); - glGenBuffers(1, &UniformBufferForDirectionalLight); - glBindBuffer(GL_UNIFORM_BUFFER, UniformBufferForDirectionalLight); - glBufferData(GL_UNIFORM_BUFFER, UBufferForDirectionalLightSize, nullptr, GL_STATIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - glBindBufferRange(GL_UNIFORM_BUFFER, UniformBufferCount++, UniformBufferForDirectionalLight, 0, UBufferForDirectionalLightSize); - } - else + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, CullingLODCountersBuffer)); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(unsigned int) * 40, nullptr, GL_DYNAMIC_DRAW)); + + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, FrustumInfoBuffer)); + + std::vector FrustumData; + for (size_t i = 0; i < 32; i++) { - glGenBuffers(1, &UniformBufferForLights); - glBindBuffer(GL_UNIFORM_BUFFER, UniformBufferForLights); - glBufferData(GL_UNIFORM_BUFFER, FE_MAX_LIGHTS * UBufferForLightSize, nullptr, GL_STATIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - glBindBufferRange(GL_UNIFORM_BUFFER, UniformBufferCount++, UniformBufferForLights, 0, FE_MAX_LIGHTS * UBufferForLightSize); - - glGenBuffers(1, &UniformBufferForDirectionalLight); - glBindBuffer(GL_UNIFORM_BUFFER, UniformBufferForDirectionalLight); - glBufferData(GL_UNIFORM_BUFFER, UBufferForDirectionalLightSize, nullptr, GL_STATIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - glBindBufferRange(GL_UNIFORM_BUFFER, UniformBufferCount++, UniformBufferForDirectionalLight, 0, UBufferForDirectionalLightSize); - - // Instanced lines - LinesBuffer.resize(FE_MAX_LINES); - - const float QuadVertices[] = { - 0.0f, -0.5f, 0.0f, - 1.0f, -0.5f, 1.0f, - 1.0f, 0.5f, 1.0f, - - 0.0f, -0.5f, 0.0f, - 1.0f, 0.5f, 1.0f, - 0.0f, 0.5f, 0.0f, - }; - glGenVertexArrays(1, &InstancedLineVAO); - glBindVertexArray(InstancedLineVAO); - - unsigned int QuadVBO; - glGenBuffers(1, &QuadVBO); - glBindBuffer(GL_ARRAY_BUFFER, QuadVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(QuadVertices), QuadVertices, GL_STATIC_DRAW); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr); - - glGenBuffers(1, &InstancedLineBuffer); - glBindBuffer(GL_ARRAY_BUFFER, InstancedLineBuffer); - glBufferData(GL_ARRAY_BUFFER, LinesBuffer.size() * sizeof(FELine), LinesBuffer.data(), GL_DYNAMIC_DRAW); - - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(FELine), static_cast(nullptr)); - glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(FELine), (void*)(3 * sizeof(float))); - glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(FELine), (void*)(6 * sizeof(float))); - glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(FELine), (void*)(9 * sizeof(float))); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glVertexAttribDivisor(0, 0); - glVertexAttribDivisor(1, 1); - glVertexAttribDivisor(2, 1); - glVertexAttribDivisor(3, 1); - glVertexAttribDivisor(4, 1); - - glBindVertexArray(0); - - SkyDome = RESOURCE_MANAGER.CreateEntity(RESOURCE_MANAGER.GetGameModel("17271E603508013IO77931TY"/*"skyDomeGameModel"*/), "skyDomeEntity"); - RESOURCE_MANAGER.MakePrefabStandard(SkyDome->Prefab); - SkyDome->bVisible = false; - SkyDome->Transform.SetScale(glm::vec3(50.0f)); - - FrustumCullingShader = RESOURCE_MANAGER.CreateShader("FE_FrustumCullingShader", - nullptr, nullptr, - nullptr, nullptr, - nullptr, RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//ComputeShaders//FE_FrustumCulling_CS.glsl").c_str()).c_str()); - - FE_GL_ERROR(glGenBuffers(1, &FrustumInfoBuffer)); - FE_GL_ERROR(glGenBuffers(1, &CullingLODCountersBuffer)); - - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, CullingLODCountersBuffer)); - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(unsigned int) * 40, nullptr, GL_DYNAMIC_DRAW)); - - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, FrustumInfoBuffer)); - - std::vector FrustumData; - for (size_t i = 0; i < 32; i++) - { - FrustumData.push_back(0.0); - } + FrustumData.push_back(0.0); + } - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(float) * (32), FrustumData.data(), GL_DYNAMIC_DRAW)); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(float) * (32), FrustumData.data(), GL_DYNAMIC_DRAW)); - ComputeTextureCopy = RESOURCE_MANAGER.CreateShader("FE_ComputeTextureCopy", - nullptr, nullptr, - nullptr, nullptr, - nullptr, RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//ComputeShaders//FE_ComputeTextureCopy_CS.glsl").c_str()).c_str()); + ComputeTextureCopy = RESOURCE_MANAGER.CreateShader("FE_ComputeTextureCopy", + nullptr, nullptr, + nullptr, nullptr, + nullptr, RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//ComputeShaders//FE_ComputeTextureCopy_CS.glsl").c_str()).c_str()); - ComputeDepthPyramidDownSample = RESOURCE_MANAGER.CreateShader("FE_ComputeDepthPyramidDownSample", - nullptr, nullptr, - nullptr, nullptr, - nullptr, RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//ComputeShaders//FE_ComputeDepthPyramidDownSample_CS.glsl").c_str()).c_str()); + ComputeDepthPyramidDownSample = RESOURCE_MANAGER.CreateShader("FE_ComputeDepthPyramidDownSample", + nullptr, nullptr, + nullptr, nullptr, + nullptr, RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//ComputeShaders//FE_ComputeDepthPyramidDownSample_CS.glsl").c_str()).c_str()); - ComputeDepthPyramidDownSample->UpdateParameterData("scaleDownBy", 2); - } -} + ComputeDepthPyramidDownSample->UpdateUniformData("scaleDownBy", 2); -void FERenderer::StandardFBInit(const int WindowWidth, const int WindowHeight) -{ - SceneToTextureFB = RESOURCE_MANAGER.CreateFramebuffer(FE_COLOR_ATTACHMENT | FE_DEPTH_ATTACHMENT, WindowWidth, WindowHeight); + FEPostProcess::ScreenQuad = RESOURCE_MANAGER.GetMesh("1Y251E6E6T78013635793156"/*"plane"*/); + FEPostProcess::ScreenQuadShader = RESOURCE_MANAGER.GetShader("7933272551311F3A1A5B2363"/*"FEScreenQuadShader"*/); - if (bSimplifiedRendering) - return; + FEShader* BloomThresholdShader = RESOURCE_MANAGER.CreateShader("FEBloomThreshold", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_Bloom_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_BloomThreshold_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "0C19574118676C2E5645200E"); + RESOURCE_MANAGER.SetTagIternal(BloomThresholdShader, ENGINE_RESOURCE_TAG); - GBuffer = new FEGBuffer(SceneToTextureFB); - SSAO = new FESSAO(SceneToTextureFB); - DebugOutputTextures["SSAO FrameBuffer"] = []() { return RENDERER.SSAO->FB->GetColorAttachment(); }; + FEShader* BloomBlurShader = RESOURCE_MANAGER.CreateShader("FEBloomBlur", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_Bloom_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_BloomBlur_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "7F3E4F5C130B537F0846274F"); + RESOURCE_MANAGER.SetTagIternal(BloomBlurShader, ENGINE_RESOURCE_TAG); - DebugOutputTextures["albedoRenderTarget"] = []() { return RENDERER.GBuffer->Albedo; }; - DebugOutputTextures["positionsRenderTarget"] = []() { return RENDERER.GBuffer->Positions; }; - DebugOutputTextures["normalsRenderTarget"] = []() { return RENDERER.GBuffer->Normals; }; - DebugOutputTextures["materialPropertiesRenderTarget"] = []() { return RENDERER.GBuffer->MaterialProperties; }; - DebugOutputTextures["shaderPropertiesRenderTarget"] = []() { return RENDERER.GBuffer->ShaderProperties; }; + FEShader* BloomCompositionShader = RESOURCE_MANAGER.CreateShader("FEBloomComposition", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_Bloom_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_Bloom//FE_BloomComposition_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "1833272551376C2E5645200E"); + RESOURCE_MANAGER.SetTagIternal(BloomCompositionShader, ENGINE_RESOURCE_TAG); - DebugOutputTextures["CSM0"] = []() { return RENDERER.CSM0; }; - DebugOutputTextures["CSM1"] = []() { return RENDERER.CSM1; }; - DebugOutputTextures["CSM2"] = []() { return RENDERER.CSM2; }; - DebugOutputTextures["CSM3"] = []() { return RENDERER.CSM3; }; + FEShader* GammaHDRShader = RESOURCE_MANAGER.CreateShader("FEGammaAndHDRShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_GammaAndHDRCorrection//FE_Gamma_and_HDR_Correction_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_GammaAndHDRCorrection//FE_Gamma_and_HDR_Correction_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "3417497A5E0C0C2A07456E44"); + RESOURCE_MANAGER.SetTagIternal(GammaHDRShader, ENGINE_RESOURCE_TAG); - DepthPyramid = RESOURCE_MANAGER.CreateTexture(); - RESOURCE_MANAGER.Textures.erase(DepthPyramid->GetObjectID()); + FEShader* FEFXAAShader = RESOURCE_MANAGER.CreateShader("FEFXAAShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_FXAA//FE_FXAA_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_FXAA//FE_FXAA_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "1E69744A10604C2A1221426B"); + RESOURCE_MANAGER.SetTagIternal(FEFXAAShader, ENGINE_RESOURCE_TAG); - DepthPyramid->Bind(); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + FEShader* DOFShader = RESOURCE_MANAGER.CreateShader("DOF", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_DOF//FE_DOF_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_DOF//FE_DOF_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "7800253C244442155D0F3C7B"); + RESOURCE_MANAGER.SetTagIternal(DOFShader, ENGINE_RESOURCE_TAG); - const int MaxDimention = std::max(WindowWidth, WindowHeight); - const size_t MipCount = static_cast(floor(log2(MaxDimention)) + 1); - FE_GL_ERROR(glTexStorage2D(GL_TEXTURE_2D, static_cast(MipCount), GL_R32F, WindowWidth, WindowHeight)); - DepthPyramid->Width = WindowWidth; - DepthPyramid->Height = WindowHeight; + FEShader* ChromaticAberrationShader = RESOURCE_MANAGER.CreateShader("chromaticAberrationShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_ChromaticAberration//FE_ChromaticAberration_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_ChromaticAberration//FE_ChromaticAberration_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "9A41665B5E2B05321A332D09"); + RESOURCE_MANAGER.SetTagIternal(ChromaticAberrationShader, ENGINE_RESOURCE_TAG); + + FEShader* FESSAOShader = RESOURCE_MANAGER.CreateShader("FESSAOShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_SSAO//FE_SSAO_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_SSAO//FE_SSAO_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "1037115B676E383E36345079"); + + RESOURCE_MANAGER.SetTagIternal(FESSAOShader, ENGINE_RESOURCE_TAG); + + FEShader* FESSAOBlurShader = RESOURCE_MANAGER.CreateShader("FESSAOBlurShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_ScreenQuad_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//PostProcessEffects//FE_SSAO//FE_SSAO_Blur_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "0B5770660B6970800D776542"); + RESOURCE_MANAGER.SetTagIternal(FESSAOBlurShader, ENGINE_RESOURCE_TAG); + + RENDERER.ShadowMapMaterial = RESOURCE_MANAGER.CreateMaterial("shadowMapMaterial", "7C41565B2E2B05321A182D89" /*"FEShadowMapShader"*/); + RENDERER.ShadowMapMaterial->Shader = RESOURCE_MANAGER.CreateShader("FEShadowMapShader", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//ShadowMapMaterial//FE_ShadowMap_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//ShadowMapMaterial//FE_ShadowMap_FS.glsl").c_str()).c_str()); + + RESOURCE_MANAGER.SetTagIternal(RENDERER.ShadowMapMaterial->Shader, ENGINE_RESOURCE_TAG); + RESOURCE_MANAGER.SetTagIternal(RENDERER.ShadowMapMaterial, ENGINE_RESOURCE_TAG); + + RENDERER.ShadowMapMaterialInstanced = RESOURCE_MANAGER.CreateMaterial("shadowMapMaterialInstanced", "5634765B2E2A05321A182D1A"/*"FEShadowMapShaderInstanced"*/); + RENDERER.ShadowMapMaterialInstanced->Shader = RESOURCE_MANAGER.CreateShader("FEShadowMapShaderInstanced", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//ShadowMapMaterial//FE_ShadowMap_INSTANCED_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//ShadowMapMaterial//FE_ShadowMap_FS.glsl").c_str()).c_str()); + + RESOURCE_MANAGER.SetTagIternal(RENDERER.ShadowMapMaterialInstanced->Shader, ENGINE_RESOURCE_TAG); + RESOURCE_MANAGER.SetTagIternal(RENDERER.ShadowMapMaterialInstanced, ENGINE_RESOURCE_TAG); } -void FERenderer::LoadStandardParams(FEShader* Shader, const FEBasicCamera* CurrentCamera, FEMaterial* Material, const FETransformComponent* Transform, const bool IsReceivingShadows, const bool IsUniformLighting) +void FERenderer::LoadStandardUniforms(FEShader* Shader, FEMaterial* Material, FETransformComponent* Transform, FEEntity* Camera, const bool IsReceivingShadows, const bool IsUniformLighting) { - static int FETextureBindingsUniformLocationsHash = static_cast(std::hash{}("textureBindings[0]")); - static int FETextureChannelsBindingsUniformLocationsHash = static_cast(std::hash{}("textureChannels[0]")); - - if (Material != nullptr) + if (Camera != nullptr) { - if (Shader->bMaterialTexturesList) - { - Shader->LoadIntArray(FETextureBindingsUniformLocationsHash, Material->TextureBindings.data(), Material->TextureBindings.size()); - Shader->LoadIntArray(FETextureChannelsBindingsUniformLocationsHash, Material->TextureChannels.data(), Material->TextureChannels.size()); - } - } + FECameraComponent& CurrentCameraComponent = Camera->GetComponent(); + FETransformComponent& CurrentCameraTransformComponent = Camera->GetComponent(); - if (Shader->GetParameter("FEWorldMatrix") != nullptr) - Shader->UpdateParameterData("FEWorldMatrix", Transform->GetTransformMatrix()); + if (Shader->GetUniform("FEViewMatrix") != nullptr) + Shader->UpdateUniformData("FEViewMatrix", CurrentCameraComponent.GetViewMatrix()); - if (Shader->GetParameter("FEViewMatrix") != nullptr) - Shader->UpdateParameterData("FEViewMatrix", CurrentCamera->GetViewMatrix()); + if (Shader->GetUniform("FEProjectionMatrix") != nullptr) + Shader->UpdateUniformData("FEProjectionMatrix", CurrentCameraComponent.GetProjectionMatrix()); - if (Shader->GetParameter("FEProjectionMatrix") != nullptr) - Shader->UpdateParameterData("FEProjectionMatrix", CurrentCamera->GetProjectionMatrix()); + if (Shader->GetUniform("FEPreviousFrameViewMatrix") != nullptr) + Shader->UpdateUniformData("FEPreviousFrameViewMatrix", CurrentCameraComponent.PreviousFrameViewMatrix); - if (Shader->GetParameter("FEPVMMatrix") != nullptr) - Shader->UpdateParameterData("FEPVMMatrix", CurrentCamera->GetProjectionMatrix() * CurrentCamera->GetViewMatrix() * Transform->GetTransformMatrix()); + if (Shader->GetUniform("ScreenSize") != nullptr) + Shader->UpdateUniformData("ScreenSize", glm::vec2(CurrentCameraComponent.GetRenderTargetWidth(), CurrentCameraComponent.GetRenderTargetHeight())); - if (Shader->GetParameter("FECameraPosition") != nullptr) - Shader->UpdateParameterData("FECameraPosition", CurrentCamera->GetPosition()); + if (Shader->GetUniform("FEPVMMatrix") != nullptr) + Shader->UpdateUniformData("FEPVMMatrix", CurrentCameraComponent.GetProjectionMatrix() * CurrentCameraComponent.GetViewMatrix() * Transform->GetWorldMatrix()); - if (Shader->GetParameter("FEGamma") != nullptr) - Shader->UpdateParameterData("FEGamma", CurrentCamera->GetGamma()); + if (Shader->GetUniform("FECameraPosition") != nullptr) + Shader->UpdateUniformData("FECameraPosition", CurrentCameraTransformComponent.GetPosition(FE_WORLD_SPACE)); - if (Shader->GetParameter("FEExposure") != nullptr) - Shader->UpdateParameterData("FEExposure", CurrentCamera->GetExposure()); + if (Shader->GetUniform("FEGamma") != nullptr) + Shader->UpdateUniformData("FEGamma", CurrentCameraComponent.GetGamma()); + + if (Shader->GetUniform("FEExposure") != nullptr) + Shader->UpdateUniformData("FEExposure", CurrentCameraComponent.GetExposure()); + } + + // TODO: Maybe it should be removed from here, because material->bind() should handle it. + if (Shader->GetUniform("textureBindings") != nullptr) + Shader->GetUniform("textureBindings")->SetValue>(Material->TextureBindings); - if (Shader->GetParameter("FEReceiveShadows") != nullptr) - Shader->UpdateParameterData("FEReceiveShadows", IsReceivingShadows); + // TODO: Maybe it should be removed from here, because material->bind() should handle it. + if (Shader->GetUniform("textureChannels") != nullptr) + Shader->GetUniform("textureChannels")->SetValue>(Material->TextureChannels); - if (Shader->GetParameter("FEUniformLighting") != nullptr) - Shader->UpdateParameterData("FEUniformLighting", IsUniformLighting); + if (Shader->GetUniform("FEWorldMatrix") != nullptr) + Shader->UpdateUniformData("FEWorldMatrix", Transform->GetWorldMatrix()); + if (Shader->GetUniform("FEReceiveShadows") != nullptr) + Shader->UpdateUniformData("FEReceiveShadows", IsReceivingShadows); + + if (Shader->GetUniform("FEUniformLighting") != nullptr) + Shader->UpdateUniformData("FEUniformLighting", IsUniformLighting); + + // TODO: Maybe it should be removed from here, because material->bind() should handle it. if (Material != nullptr) { - if (Shader->GetParameter("FEAOIntensity") != nullptr) - Shader->UpdateParameterData("FEAOIntensity", Material->GetAmbientOcclusionIntensity()); + if (Shader->GetUniform("FEAOIntensity") != nullptr) + Shader->UpdateUniformData("FEAOIntensity", Material->GetAmbientOcclusionIntensity()); - if (Shader->GetParameter("FEAOMapIntensity") != nullptr) - Shader->UpdateParameterData("FEAOMapIntensity", Material->GetAmbientOcclusionMapIntensity()); + if (Shader->GetUniform("FEAOMapIntensity") != nullptr) + Shader->UpdateUniformData("FEAOMapIntensity", Material->GetAmbientOcclusionMapIntensity()); - if (Shader->GetParameter("FENormalMapIntensity") != nullptr) - Shader->UpdateParameterData("FENormalMapIntensity", Material->GetNormalMapIntensity()); + if (Shader->GetUniform("FENormalMapIntensity") != nullptr) + Shader->UpdateUniformData("FENormalMapIntensity", Material->GetNormalMapIntensity()); - if (Shader->GetParameter("FERoughness") != nullptr) - Shader->UpdateParameterData("FERoughness", Material->Roughness); + if (Shader->GetUniform("FERoughness") != nullptr) + Shader->UpdateUniformData("FERoughness", Material->Roughness); - if (Shader->GetParameter("FERoughnessMapIntensity") != nullptr) - Shader->UpdateParameterData("FERoughnessMapIntensity", Material->GetRoughnessMapIntensity()); + if (Shader->GetUniform("FERoughnessMapIntensity") != nullptr) + Shader->UpdateUniformData("FERoughnessMapIntensity", Material->GetRoughnessMapIntensity()); - if (Shader->GetParameter("FEMetalness") != nullptr) - Shader->UpdateParameterData("FEMetalness", Material->Metalness); + if (Shader->GetUniform("FEMetalness") != nullptr) + Shader->UpdateUniformData("FEMetalness", Material->Metalness); - if (Shader->GetParameter("FEMetalnessMapIntensity") != nullptr) - Shader->UpdateParameterData("FEMetalnessMapIntensity", Material->GetMetalnessMapIntensity()); + if (Shader->GetUniform("FEMetalnessMapIntensity") != nullptr) + Shader->UpdateUniformData("FEMetalnessMapIntensity", Material->GetMetalnessMapIntensity()); - if (Shader->GetParameter("FETiling") != nullptr) - Shader->UpdateParameterData("FETiling", Material->GetTiling()); + if (Shader->GetUniform("FETiling") != nullptr) + Shader->UpdateUniformData("FETiling", Material->GetTiling()); - if (Shader->GetParameter("compactMaterialPacking") != nullptr) - Shader->UpdateParameterData("compactMaterialPacking", Material->IsCompackPacking()); + if (Shader->GetUniform("compactMaterialPacking") != nullptr) + Shader->UpdateUniformData("compactMaterialPacking", Material->IsCompackPacking()); } } -void FERenderer::LoadStandardParams(FEShader* Shader, const FEBasicCamera* CurrentCamera, const bool IsReceivingShadows, const bool IsUniformLighting) +void FERenderer::LoadStandardUniforms(FEShader* Shader, const bool IsReceivingShadows, FEEntity* Camera, const bool IsUniformLighting) { - if (Shader->GetParameter("FEViewMatrix") != nullptr) - Shader->UpdateParameterData("FEViewMatrix", CurrentCamera->GetViewMatrix()); + if (Camera != nullptr) + { + FECameraComponent& CurrentCameraComponent = Camera->GetComponent(); + FETransformComponent& CurrentCameraTransformComponent = Camera->GetComponent(); - if (Shader->GetParameter("FEProjectionMatrix") != nullptr) - Shader->UpdateParameterData("FEProjectionMatrix", CurrentCamera->GetProjectionMatrix()); + if (Shader->GetUniform("FEViewMatrix") != nullptr) + Shader->UpdateUniformData("FEViewMatrix", CurrentCameraComponent.GetViewMatrix()); - if (Shader->GetParameter("FECameraPosition") != nullptr) - Shader->UpdateParameterData("FECameraPosition", CurrentCamera->GetPosition()); + if (Shader->GetUniform("FEProjectionMatrix") != nullptr) + Shader->UpdateUniformData("FEProjectionMatrix", CurrentCameraComponent.GetProjectionMatrix()); - if (Shader->GetParameter("FEGamma") != nullptr) - Shader->UpdateParameterData("FEGamma", CurrentCamera->GetGamma()); + if (Shader->GetUniform("FECameraPosition") != nullptr) + Shader->UpdateUniformData("FECameraPosition", CurrentCameraTransformComponent.GetPosition(FE_WORLD_SPACE)); - if (Shader->GetParameter("FEExposure") != nullptr) - Shader->UpdateParameterData("FEExposure", CurrentCamera->GetExposure()); + if (Shader->GetUniform("FEGamma") != nullptr) + Shader->UpdateUniformData("FEGamma", CurrentCameraComponent.GetGamma()); - if (Shader->GetParameter("FEReceiveShadows") != nullptr) - Shader->UpdateParameterData("FEReceiveShadows", IsReceivingShadows); + if (Shader->GetUniform("FEExposure") != nullptr) + Shader->UpdateUniformData("FEExposure", CurrentCameraComponent.GetExposure()); + } + + if (Shader->GetUniform("FEReceiveShadows") != nullptr) + Shader->UpdateUniformData("FEReceiveShadows", IsReceivingShadows); - if (Shader->GetParameter("FEUniformLighting") != nullptr) - Shader->UpdateParameterData("FEUniformLighting", IsUniformLighting); + if (Shader->GetUniform("FEUniformLighting") != nullptr) + Shader->UpdateUniformData("FEUniformLighting", IsUniformLighting); } -void FERenderer::AddPostProcess(FEPostProcess* NewPostProcess, const bool NoProcessing) +void FERenderer::AddPostProcess(FECameraRenderingData* CameraRenderingData, FEPostProcess* NewPostProcess, const bool NoProcessing) { - PostProcessEffects.push_back(NewPostProcess); + if (CameraRenderingData == nullptr) + return; + + CameraRenderingData->PostProcessEffects.push_back(NewPostProcess); if (NoProcessing) return; - for (size_t i = 0; i < PostProcessEffects.back()->Stages.size(); i++) + for (size_t i = 0; i < CameraRenderingData->PostProcessEffects.back()->Stages.size(); i++) { - PostProcessEffects.back()->Stages[i]->InTexture.resize(PostProcessEffects.back()->Stages[i]->InTextureSource.size()); + CameraRenderingData->PostProcessEffects.back()->Stages[i]->InTexture.resize(CameraRenderingData->PostProcessEffects.back()->Stages[i]->InTextureSource.size()); //to-do: change when out texture could be different resolution or/and format. //#fix - if (i == PostProcessEffects.back()->Stages.size() - 1) + if (i == CameraRenderingData->PostProcessEffects.back()->Stages.size() - 1) { - PostProcessEffects.back()->Stages[i]->OutTexture = RESOURCE_MANAGER.CreateSameFormatTexture(SceneToTextureFB->GetColorAttachment()); + CameraRenderingData->PostProcessEffects.back()->Stages[i]->OutTexture = RESOURCE_MANAGER.CreateSameFormatTexture(CameraRenderingData->SceneToTextureFB->GetColorAttachment()); } else { - const int FinalW = PostProcessEffects.back()->ScreenWidth; - const int FinalH = PostProcessEffects.back()->ScreenHeight; - PostProcessEffects.back()->Stages[i]->OutTexture = RESOURCE_MANAGER.CreateSameFormatTexture(SceneToTextureFB->GetColorAttachment(), FinalW, FinalH); + const int FinalW = CameraRenderingData->PostProcessEffects.back()->ScreenWidth; + const int FinalH = CameraRenderingData->PostProcessEffects.back()->ScreenHeight; + CameraRenderingData->PostProcessEffects.back()->Stages[i]->OutTexture = RESOURCE_MANAGER.CreateSameFormatTexture(CameraRenderingData->SceneToTextureFB->GetColorAttachment(), FinalW, FinalH); } - PostProcessEffects.back()->TexturesToDelete.push_back(PostProcessEffects.back()->Stages[i]->OutTexture); + CameraRenderingData->PostProcessEffects.back()->TexturesToDelete.push_back(CameraRenderingData->PostProcessEffects.back()->Stages[i]->OutTexture); } } -void FERenderer::LoadUniformBlocks() +void FERenderer::LoadUniformBlocks(FEScene* CurrentScene) { std::vector Info; Info.resize(FE_MAX_LIGHTS); @@ -290,68 +338,54 @@ void FERenderer::LoadUniformBlocks() FEDirectionalLightShaderInfo DirectionalLightInfo; int Index = 0; - auto LightIterator = OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].begin(); - while (LightIterator != OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].end()) - { - FEDirectionalLight* Light = reinterpret_cast(LightIterator->second); - - DirectionalLightInfo.Position = glm::vec4(Light->Transform.GetPosition(), 0.0f); - DirectionalLightInfo.Color = glm::vec4(Light->GetColor() * Light->GetIntensity(), 0.0f); - DirectionalLightInfo.Direction = glm::vec4(Light->GetDirection(), 0.0f); - DirectionalLightInfo.CSM0 = Light->CascadeData[0].ProjectionMat * Light->CascadeData[0].ViewMat; - DirectionalLightInfo.CSM1 = Light->CascadeData[1].ProjectionMat * Light->CascadeData[1].ViewMat; - DirectionalLightInfo.CSM2 = Light->CascadeData[2].ProjectionMat * Light->CascadeData[2].ViewMat; - DirectionalLightInfo.CSM3 = Light->CascadeData[3].ProjectionMat * Light->CascadeData[3].ViewMat; - DirectionalLightInfo.CSMSizes = glm::vec4(Light->CascadeData[0].Size, Light->CascadeData[1].Size, Light->CascadeData[2].Size, Light->CascadeData[3].Size); - DirectionalLightInfo.ActiveCascades = Light->ActiveCascades; - DirectionalLightInfo.BiasFixed = Light->ShadowBias; - if (!Light->bStaticShadowBias) - DirectionalLightInfo.BiasFixed = -1.0f; - DirectionalLightInfo.BiasVariableIntensity = Light->ShadowBiasVariableIntensity; - DirectionalLightInfo.Intensity = Light->GetIntensity(); - - Info[Index].Position = glm::vec4(Light->Transform.GetPosition(), 0.0f); - Info[Index].Color = glm::vec4(Light->GetColor() * Light->GetIntensity(), 0.0f); - - Index++; - LightIterator++; - } - - LightIterator = OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT].begin(); - while (LightIterator != OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT].end()) + std::vector< std::string> LightsIDList = CurrentScene->GetEntityIDListWithComponent(); + for (size_t i = 0; i < LightsIDList.size(); i++) { - FESpotLight* Light = reinterpret_cast(LightIterator->second); + FEEntity* LightEntity = CurrentScene->GetEntity(LightsIDList[i]); + FETransformComponent& TransformComponent = LightEntity->GetComponent(); + FELightComponent& LightComponent = LightEntity->GetComponent(); - Info[Index].TypeAndAngles = glm::vec3(Light->GetType(), - glm::cos(glm::radians(Light->GetSpotAngle())), - glm::cos(glm::radians(Light->GetSpotAngleOuter()))); - - Info[Index].Direction = glm::vec4(Light->GetDirection(), 0.0f); - - Info[Index].Position = glm::vec4(Light->Transform.GetPosition(), 0.0f); - Info[Index].Color = glm::vec4(Light->GetColor() * Light->GetIntensity(), 0.0f); - - Index++; - LightIterator++; - } + if (LightComponent.GetType() == FE_DIRECTIONAL_LIGHT) + { + DirectionalLightInfo.Position = glm::vec4(TransformComponent.GetPosition(), 0.0f); + DirectionalLightInfo.Color = glm::vec4(LightComponent.GetColor() * LightComponent.GetIntensity(), 0.0f); + DirectionalLightInfo.Direction = glm::vec4(LIGHT_SYSTEM.GetDirection(LightEntity), 0.0f); + DirectionalLightInfo.CSM0 = LightComponent.CascadeData[0].ProjectionMat * LightComponent.CascadeData[0].ViewMat; + DirectionalLightInfo.CSM1 = LightComponent.CascadeData[1].ProjectionMat * LightComponent.CascadeData[1].ViewMat; + DirectionalLightInfo.CSM2 = LightComponent.CascadeData[2].ProjectionMat * LightComponent.CascadeData[2].ViewMat; + DirectionalLightInfo.CSM3 = LightComponent.CascadeData[3].ProjectionMat * LightComponent.CascadeData[3].ViewMat; + DirectionalLightInfo.CSMSizes = glm::vec4(LightComponent.CascadeData[0].Size, LightComponent.CascadeData[1].Size, LightComponent.CascadeData[2].Size, LightComponent.CascadeData[3].Size); + DirectionalLightInfo.ActiveCascades = LightComponent.ActiveCascades; + DirectionalLightInfo.BiasFixed = LightComponent.ShadowBias; + if (!LightComponent.bStaticShadowBias) + DirectionalLightInfo.BiasFixed = -1.0f; + DirectionalLightInfo.BiasVariableIntensity = LightComponent.ShadowBiasVariableIntensity; + DirectionalLightInfo.Intensity = LightComponent.GetIntensity(); + } + else if (LightComponent.GetType() == FE_SPOT_LIGHT) + { + Info[Index].TypeAndAngles = glm::vec4(LightComponent.GetType(), + glm::cos(glm::radians(LightComponent.GetSpotAngle())), + glm::cos(glm::radians(LightComponent.GetSpotAngleOuter())), + 0.0f); - LightIterator = OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT].begin(); - while (LightIterator != OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT].end()) - { - FEPointLight* Light = reinterpret_cast(LightIterator->second); - Info[Index].TypeAndAngles = glm::vec3(Light->GetType(), 0.0f, 0.0f); + Info[Index].Direction = glm::vec4(LIGHT_SYSTEM.GetDirection(LightEntity), 0.0f); + } + else if (LightComponent.GetType() == FE_POINT_LIGHT) + { + Info[Index].TypeAndAngles = glm::vec4(LightComponent.GetType(), 0.0f, 0.0f, 0.0f); + } - Info[Index].Position = glm::vec4(Light->Transform.GetPosition(), 0.0f); - Info[Index].Color = glm::vec4(Light->GetColor() * Light->GetIntensity(), 0.0f); + Info[Index].Position = glm::vec4(TransformComponent.GetPosition(), 0.0f); + Info[Index].Color = glm::vec4(LightComponent.GetColor() * LightComponent.GetIntensity(), 0.0f); Index++; - LightIterator++; } //#fix only standardShaders uniforms buffers are filled. static int LightInfoHash = static_cast(std::hash{}("lightInfo")); static int DirectionalLightInfoHash = static_cast(std::hash{}("directionalLightInfo")); - const std::vector ShaderList = RESOURCE_MANAGER.GetStandardShadersList(); + const std::vector ShaderList = RESOURCE_MANAGER.GetEnginePrivateShaderIDList(); for (size_t i = 0; i < ShaderList.size(); i++) { FEShader* Shader = RESOURCE_MANAGER.GetShader(ShaderList[i]); @@ -364,10 +398,10 @@ void FERenderer::LoadUniformBlocks() if (IteratorBlock->second == GL_INVALID_INDEX) IteratorBlock->second = UniformBufferForLights; // adding 4 because vec3 in shader buffer will occupy 16 bytes not 12. - const size_t SizeOfFELightShaderInfo = sizeof(FELightShaderInfo) + 4; + const size_t SizeOfFELightShaderInfo = sizeof(FELightShaderInfo);// +4; FE_GL_ERROR(glBindBuffer(GL_UNIFORM_BUFFER, IteratorBlock->second)); - //FE_GL_ERROR(glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeOfFELightShaderInfo * info.size(), &info)); + //FE_GL_ERROR(glBufferSubData(GL_UNIFORM_BUFFER, 0, SizeOfFELightShaderInfo * Info.size(), &Info)); for (size_t j = 0; j < Info.size(); j++) { FE_GL_ERROR(glBufferSubData(GL_UNIFORM_BUFFER, j * SizeOfFELightShaderInfo, SizeOfFELightShaderInfo, &Info[j])); @@ -391,224 +425,147 @@ void FERenderer::LoadUniformBlocks() } } -void FERenderer::RenderEntityInstanced(FEEntityInstanced* EntityInstanced, FEBasicCamera* CurrentCamera, float** Frustum, const bool bShadowMap, const bool bReloadUniformBlocks, const int ComponentIndex) +void FERenderer::RenderGameModelComponentWithInstanced(FEEntity* Entity, FEEntity* Camera, bool bShadowMap, bool bReloadUniformBlocks, size_t PrefabIndex) { - if (bReloadUniformBlocks) - LoadUniformBlocks(); + if (Entity == nullptr || !Entity->HasComponent()) + return; - if (ComponentIndex != -1) - { - GPUCulling(EntityInstanced, static_cast(ComponentIndex), CurrentCamera); + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; - FEGameModel* CurrentGameModel = EntityInstanced->Prefab->Components[ComponentIndex]->GameModel; + FETransformComponent& TransformComponent = Entity->GetComponent(); + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + FEEntity* EntityWithGameModel = INSTANCED_RENDERING_SYSTEM.GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[PrefabIndex]->EntityIDWithGameModelComponent); + if (EntityWithGameModel == nullptr) + return; - FEShader* OriginalShader = CurrentGameModel->GetMaterial()->Shader; - if (OriginalShader->GetName() == "FEPBRShader") - { - if (ShaderToForce) - { - CurrentGameModel->GetMaterial()->Shader = ShaderToForce; - } - else - { - CurrentGameModel->GetMaterial()->Shader = RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/); - } - } + FEGameModelComponent& GameModelComponent = EntityWithGameModel->GetComponent(); - CurrentGameModel->GetMaterial()->Bind(); - const FETransformComponent TempTransform = EntityInstanced->Transform.Combine(EntityInstanced->Prefab->Components[ComponentIndex]->Transform); - LoadStandardParams(CurrentGameModel->GetMaterial()->Shader, CurrentCamera, CurrentGameModel->Material, &TempTransform, EntityInstanced->IsReceivingShadows(), EntityInstanced->IsUniformLighting()); - CurrentGameModel->GetMaterial()->Shader->LoadDataToGPU(); + if (bReloadUniformBlocks) + LoadUniformBlocks(Entity->ParentScene); - EntityInstanced->Render(static_cast(ComponentIndex)); + GPUCulling(Entity, GameModelComponent, Camera, PrefabIndex); - CurrentGameModel->GetMaterial()->UnBind(); - if (OriginalShader->GetName() == "FEPBRShader") + FEGameModel* CurrentGameModel = GameModelComponent.GetGameModel(); + FEShader* OriginalShader = CurrentGameModel->GetMaterial()->Shader; + if (OriginalShader->GetName() == "FEPBRShader") + { + if (ShaderToForce) { - CurrentGameModel->GetMaterial()->Shader = OriginalShader; - if (CurrentGameModel->GetBillboardMaterial() != nullptr) - CurrentGameModel->GetBillboardMaterial()->Shader = OriginalShader; + CurrentGameModel->GetMaterial()->Shader = ShaderToForce; } - - // Billboards part - if (CurrentGameModel->GetBillboardMaterial() != nullptr) + else { - FEMaterial* RegularBillboardMaterial = CurrentGameModel->GetBillboardMaterial(); - if (bShadowMap) - { - ShadowMapMaterialInstanced->SetAlbedoMap(RegularBillboardMaterial->GetAlbedoMap()); - CurrentGameModel->SetBillboardMaterial(ShadowMapMaterialInstanced); - } - - OriginalShader = CurrentGameModel->GetMaterial()->Shader; - if (OriginalShader->GetName() == "FEPBRShader") - { - if (ShaderToForce) - { - CurrentGameModel->GetBillboardMaterial()->Shader = ShaderToForce; - } - else - { - CurrentGameModel->GetBillboardMaterial()->Shader = RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/); - } - } - - CurrentGameModel->GetBillboardMaterial()->Bind(); - LoadStandardParams(CurrentGameModel->GetBillboardMaterial()->Shader, CurrentCamera, CurrentGameModel->GetBillboardMaterial(), &EntityInstanced->Transform, EntityInstanced->IsReceivingShadows(), EntityInstanced->IsUniformLighting()); - CurrentGameModel->GetBillboardMaterial()->Shader->LoadDataToGPU(); - - EntityInstanced->RenderOnlyBillbords(CurrentCamera->GetPosition()); + CurrentGameModel->GetMaterial()->Shader = RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/); + } + } - CurrentGameModel->GetBillboardMaterial()->UnBind(); - if (OriginalShader->GetName() == "FEPBRShader") - CurrentGameModel->GetBillboardMaterial()->Shader = OriginalShader; + CurrentGameModel->GetMaterial()->Bind(); + LoadStandardUniforms(CurrentGameModel->GetMaterial()->Shader, CurrentGameModel->Material, &TransformComponent, Camera, GameModelComponent.IsReceivingShadows(), GameModelComponent.IsUniformLighting()); + CurrentGameModel->GetMaterial()->Shader->LoadUniformsDataToGPU(); - if (bShadowMap) - { - CurrentGameModel->SetBillboardMaterial(RegularBillboardMaterial); - } - } + INSTANCED_RENDERING_SYSTEM.Render(Entity, GameModelComponent, PrefabIndex); - return; + CurrentGameModel->GetMaterial()->UnBind(); + if (OriginalShader->GetName() == "FEPBRShader") + { + CurrentGameModel->GetMaterial()->Shader = OriginalShader; + if (CurrentGameModel->GetBillboardMaterial() != nullptr) + CurrentGameModel->GetBillboardMaterial()->Shader = OriginalShader; } - for (size_t i = 0; i < EntityInstanced->Prefab->Components.size(); i++) + // Billboards part + if (CurrentGameModel->GetBillboardMaterial() != nullptr) { - GPUCulling(EntityInstanced, static_cast(i), CurrentCamera); - - FEGameModel* CurrentGameModel = EntityInstanced->Prefab->Components[i]->GameModel; + FEMaterial* RegularBillboardMaterial = CurrentGameModel->GetBillboardMaterial(); + if (bShadowMap) + { + ShadowMapMaterialInstanced->SetAlbedoMap(RegularBillboardMaterial->GetAlbedoMap()); + CurrentGameModel->SetBillboardMaterial(ShadowMapMaterialInstanced); + } - FEShader* OriginalShader = CurrentGameModel->GetMaterial()->Shader; + OriginalShader = CurrentGameModel->GetMaterial()->Shader; if (OriginalShader->GetName() == "FEPBRShader") { if (ShaderToForce) { - CurrentGameModel->GetMaterial()->Shader = ShaderToForce; + CurrentGameModel->GetBillboardMaterial()->Shader = ShaderToForce; } else { - CurrentGameModel->GetMaterial()->Shader = RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/); + CurrentGameModel->GetBillboardMaterial()->Shader = RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/); } } - CurrentGameModel->GetMaterial()->Bind(); - FETransformComponent TempTransform = EntityInstanced->Transform.Combine(EntityInstanced->Prefab->Components[i]->Transform); - LoadStandardParams(CurrentGameModel->GetMaterial()->Shader, CurrentCamera, CurrentGameModel->Material, &TempTransform, EntityInstanced->IsReceivingShadows(), EntityInstanced->IsUniformLighting()); - CurrentGameModel->GetMaterial()->Shader->LoadDataToGPU(); + CurrentGameModel->GetBillboardMaterial()->Bind(); + LoadStandardUniforms(CurrentGameModel->GetBillboardMaterial()->Shader, CurrentGameModel->GetBillboardMaterial(), &TransformComponent, Camera, GameModelComponent.IsReceivingShadows(), GameModelComponent.IsUniformLighting()); + CurrentGameModel->GetBillboardMaterial()->Shader->LoadUniformsDataToGPU(); - EntityInstanced->Render(static_cast(i)); + INSTANCED_RENDERING_SYSTEM.RenderOnlyBillbords(Entity, GameModelComponent, PrefabIndex); - CurrentGameModel->GetMaterial()->UnBind(); + CurrentGameModel->GetBillboardMaterial()->UnBind(); if (OriginalShader->GetName() == "FEPBRShader") + CurrentGameModel->GetBillboardMaterial()->Shader = OriginalShader; + + if (bShadowMap) { - CurrentGameModel->GetMaterial()->Shader = OriginalShader; - if (CurrentGameModel->GetBillboardMaterial() != nullptr) - CurrentGameModel->GetBillboardMaterial()->Shader = OriginalShader; + CurrentGameModel->SetBillboardMaterial(RegularBillboardMaterial); } + } +} - // Billboards part - if (CurrentGameModel->GetBillboardMaterial() != nullptr) - { - FEMaterial* RegularBillboardMaterial = CurrentGameModel->GetBillboardMaterial(); - if (bShadowMap) - { - ShadowMapMaterialInstanced->SetAlbedoMap(RegularBillboardMaterial->GetAlbedoMap()); - CurrentGameModel->SetBillboardMaterial(ShadowMapMaterialInstanced); - } +void FERenderer::SimplifiedRender(FEScene* CurrentScene) +{ + if (CurrentScene == nullptr) + return; - OriginalShader = CurrentGameModel->GetMaterial()->Shader; - if (OriginalShader->GetName() == "FEPBRShader") - { - if (ShaderToForce) - { - CurrentGameModel->GetBillboardMaterial()->Shader = ShaderToForce; - } - else - { - CurrentGameModel->GetBillboardMaterial()->Shader = RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/); - } - } + if (CurrentScene == nullptr) + return; - CurrentGameModel->GetBillboardMaterial()->Bind(); - LoadStandardParams(CurrentGameModel->GetBillboardMaterial()->Shader, CurrentCamera, CurrentGameModel->GetBillboardMaterial(), &EntityInstanced->Transform, EntityInstanced->IsReceivingShadows(), EntityInstanced->IsUniformLighting()); - CurrentGameModel->GetBillboardMaterial()->Shader->LoadDataToGPU(); + FEEntity* MainCameraEntity = CAMERA_SYSTEM.GetMainCamera(CurrentScene); + if (MainCameraEntity == nullptr) + return; - EntityInstanced->RenderOnlyBillbords(CurrentCamera->GetPosition()); + FECameraComponent& CurrentCameraComponent = MainCameraEntity->GetComponent(); + FETransformComponent& CurrentCameraTransformComponent = MainCameraEntity->GetComponent(); + CurrentCameraComponent.UpdateFrustumPlanes(); - CurrentGameModel->GetBillboardMaterial()->UnBind(); - if (OriginalShader->GetName() == "FEPBRShader") - CurrentGameModel->GetBillboardMaterial()->Shader = OriginalShader; + CurrentCameraRenderingData->SceneToTextureFB->Bind(); - if (bShadowMap) - { - CurrentGameModel->SetBillboardMaterial(RegularBillboardMaterial); - } - } + if (CurrentCameraComponent.IsClearColorEnabled()) + { + glm::vec4 ClearColor = CurrentCameraComponent.GetClearColor(); + glClearColor(ClearColor.x, ClearColor.y, ClearColor.z, ClearColor.w); + FE_GL_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } -} -void FERenderer::SimplifiedRender(FEBasicCamera* CurrentCamera) -{ - CurrentCamera->UpdateFrustumPlanes(); + entt::basic_group GameModelGroup = CurrentScene->Registry.group(entt::get); + for (entt::entity EnTTEntity : GameModelGroup) + { + auto& [GameModelComponent, TransformComponent] = GameModelGroup.get(EnTTEntity); - SceneToTextureFB->Bind(); - //glClearColor(0.55f, 0.73f, 0.87f, 1.0f); - - if (bClearActiveInSimplifiedRendering) - FE_GL_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + if (!GameModelComponent.IsVisible() /*|| !GameModelComponent.IsPostprocessApplied()*/) + continue; - auto EntityIterator = SCENE.EntityMap.begin(); - while (EntityIterator != SCENE.EntityMap.end()) - { - auto entity = EntityIterator->second; + FEEntity* Entity = CurrentScene->GetEntityByEnTT(EnTTEntity); + if (Entity == nullptr) + continue; - if (entity->IsVisible() && entity->IsPostprocessApplied()) + if (!Entity->HasComponent()) { - if (entity->GetType() == FE_ENTITY) - { - //ForceShader(RESOURCE_MANAGER.GetShader("670B01496E202658377A4576"/*"FEPBRGBufferShader"*/)); - RenderEntityForward(entity, CurrentCamera); - } - else if (entity->GetType() == FE_ENTITY_INSTANCED) - { - //ForceShader(RESOURCE_MANAGER.GetShader("613830232E12602D6A1D2C17"/*"FEPBRInstancedGBufferShader"*/)); - //RenderEntityInstanced(reinterpret_cast(entity), CurrentCamera, CurrentCamera->GetFrustumPlanes(), false); - } + RenderGameModelComponentForward(Entity, MainCameraEntity); + } + // No instanced rendering for now. + else if (Entity->HasComponent()) + { + //ForceShader(RESOURCE_MANAGER.GetShader("613830232E12602D6A1D2C17"/*"FEPBRInstancedGBufferShader"*/)); + //RenderGameModelComponentWithInstanced(Entity); } - - EntityIterator++; } - // ********* RENDER INSTANCED LINE ********* - /*FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, InstancedLineBuffer)); - FE_GL_ERROR(glBufferSubData(GL_ARRAY_BUFFER, 0, FE_MAX_LINES * sizeof(FELine), this->LinesBuffer.data())); - FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, 0)); - - InstancedLineShader->Start(); - InstancedLineShader->UpdateParameterData("FEProjectionMatrix", CurrentCamera->GetProjectionMatrix()); - InstancedLineShader->UpdateParameterData("FEViewMatrix", CurrentCamera->GetViewMatrix()); - InstancedLineShader->UpdateParameterData("resolution", glm::vec2(SceneToTextureFB->GetWidth(), SceneToTextureFB->GetHeight())); - InstancedLineShader->LoadDataToGPU(); - - FE_GL_ERROR(glBindVertexArray(InstancedLineVAO)); - FE_GL_ERROR(glEnableVertexAttribArray(0)); - FE_GL_ERROR(glEnableVertexAttribArray(1)); - FE_GL_ERROR(glEnableVertexAttribArray(2)); - FE_GL_ERROR(glEnableVertexAttribArray(3)); - FE_GL_ERROR(glEnableVertexAttribArray(4)); - FE_GL_ERROR(glDrawArraysInstanced(GL_TRIANGLES, 0, 6, LineCounter)); - FE_GL_ERROR(glDisableVertexAttribArray(0)); - FE_GL_ERROR(glDisableVertexAttribArray(1)); - FE_GL_ERROR(glDisableVertexAttribArray(2)); - FE_GL_ERROR(glDisableVertexAttribArray(3)); - FE_GL_ERROR(glDisableVertexAttribArray(4)); - FE_GL_ERROR(glBindVertexArray(0)); - InstancedLineShader->Stop();*/ - // ********* RENDER INSTANCED LINE END ********* - - SceneToTextureFB->UnBind(); - FinalScene = SceneToTextureFB->GetColorAttachment(); - FinalScene->Bind(); + CurrentCameraRenderingData->SceneToTextureFB->UnBind(); + CurrentCameraRenderingData->FinalScene = CurrentCameraRenderingData->SceneToTextureFB->GetColorAttachment(); + CurrentCameraRenderingData->FinalScene->Bind(); // ********* RENDER FRAME BUFFER TO SCREEN ********* glDepthMask(GL_FALSE); @@ -616,8 +573,9 @@ void FERenderer::SimplifiedRender(FEBasicCamera* CurrentCamera) FEShader* ScreenQuadShader = RESOURCE_MANAGER.GetShader("7933272551311F3A1A5B2363"/*"FEScreenQuadShader"*/); ScreenQuadShader->Start(); - LoadStandardParams(ScreenQuadShader, CurrentCamera, true); - ScreenQuadShader->LoadDataToGPU(); + + LoadStandardUniforms(ScreenQuadShader, true, MainCameraEntity); + ScreenQuadShader->LoadUniformsDataToGPU(); FE_GL_ERROR(glBindVertexArray(RESOURCE_MANAGER.GetMesh("1Y251E6E6T78013635793156"/*"plane"*/)->GetVaoID())); FE_GL_ERROR(glEnableVertexAttribArray(0)); @@ -631,23 +589,225 @@ void FERenderer::SimplifiedRender(FEBasicCamera* CurrentCamera) glDepthFunc(GL_LESS); // ********* RENDER FRAME BUFFER TO SCREEN END ********* - FinalScene->UnBind(); + CurrentCameraRenderingData->FinalScene->UnBind(); +} + +FECameraRenderingData* FERenderer::CreateCameraRenderingData(FEEntity* CameraEntity) +{ + FECameraRenderingData* Result = nullptr; + + if (CameraEntity == nullptr) + return Result; + + if (!CameraEntity->HasComponent()) + return Result; + + FECameraComponent& CameraComponent = CameraEntity->GetComponent(); + if (CameraComponent.GetRenderTargetWidth() <= 0 || CameraComponent.GetRenderTargetHeight() <= 0) + return Result; + + Result = new FECameraRenderingData(); + Result->CameraEntity = CameraEntity; + Result->SceneToTextureFB = RESOURCE_MANAGER.CreateFramebuffer(FE_COLOR_ATTACHMENT | FE_DEPTH_ATTACHMENT, CameraComponent.GetRenderTargetWidth(), CameraComponent.GetRenderTargetHeight()); + + if (CameraComponent.GetRenderingPipeline() == FERenderingPipeline::Forward_Simplified) + return Result; + + Result->GBuffer = new FEGBuffer(Result->SceneToTextureFB); + Result->SSAO = new FESSAO(Result->SceneToTextureFB); + + Result->DepthPyramid = RESOURCE_MANAGER.CreateTexture(); + RESOURCE_MANAGER.Textures.erase(Result->DepthPyramid->GetObjectID()); + + Result->DepthPyramid->Bind(); + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + + const int MaxDimention = std::max(CameraComponent.GetRenderTargetWidth(), CameraComponent.GetRenderTargetHeight()); + const size_t MipCount = static_cast(floor(log2(MaxDimention)) + 1); + FE_GL_ERROR(glTexStorage2D(GL_TEXTURE_2D, static_cast(MipCount), GL_R32F, CameraComponent.GetRenderTargetWidth(), CameraComponent.GetRenderTargetHeight())); + Result->DepthPyramid->Width = CameraComponent.GetRenderTargetWidth(); + Result->DepthPyramid->Height = CameraComponent.GetRenderTargetHeight(); + + glm::ivec2 ViewportSize = glm::ivec2(CameraComponent.GetRenderTargetWidth(), CameraComponent.GetRenderTargetHeight()); + if (CameraComponent.GetRenderScale() != 1.0f) + { + ViewportSize.x = CameraComponent.Viewport->GetWidth(); + ViewportSize.y = CameraComponent.Viewport->GetHeight(); + } + + // ************************************ Bloom ************************************ + FEPostProcess* BloomEffect = ENGINE.CreatePostProcess("Bloom", static_cast(ViewportSize.x / 4.0f), static_cast(ViewportSize.y / 4.0f)); + BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_SCENE_HDR_COLOR, RESOURCE_MANAGER.GetShader("0C19574118676C2E5645200E"/*"FEBloomThreshold"*/))); + BloomEffect->Stages[0]->Shader->UpdateUniformData("thresholdBrightness", 1.0f); + + FEShader* BloomBlurShader = RESOURCE_MANAGER.GetShader("7F3E4F5C130B537F0846274F"/*"FEBloomBlur"*/); + + BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, BloomBlurShader)); + BloomEffect->Stages.back()->StageSpecificUniformValues.push_back(FEShaderUniformValue("FEBlurDirection", glm::vec2(0.0f, 1.0f))); + BloomEffect->Stages.back()->StageSpecificUniformValues.push_back(FEShaderUniformValue("BloomSize", 5.0f)); + + BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, BloomBlurShader)); + BloomEffect->Stages.back()->StageSpecificUniformValues.push_back(FEShaderUniformValue("FEBlurDirection", glm::vec2(1.0f, 0.0f))); + BloomEffect->Stages.back()->StageSpecificUniformValues.push_back(FEShaderUniformValue("BloomSize", 5.0f)); + + BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, BloomBlurShader)); + BloomEffect->Stages.back()->StageSpecificUniformValues.push_back(FEShaderUniformValue("FEBlurDirection", glm::vec2(0.0f, 1.0f))); + BloomEffect->Stages.back()->StageSpecificUniformValues.push_back(FEShaderUniformValue("BloomSize", 1.0f)); + + BloomEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, BloomBlurShader)); + BloomEffect->Stages.back()->StageSpecificUniformValues.push_back(FEShaderUniformValue("FEBlurDirection", glm::vec2(1.0f, 0.0f))); + BloomEffect->Stages.back()->StageSpecificUniformValues.push_back(FEShaderUniformValue("BloomSize", 1.0f)); + + FEShader* BloomCompositionShader = RESOURCE_MANAGER.GetShader("1833272551376C2E5645200E"/*"FEBloomComposition"*/); + BloomEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, FE_POST_PROCESS_SCENE_HDR_COLOR}, BloomCompositionShader)); + + RENDERER.AddPostProcess(Result, BloomEffect); + // ************************************ Bloom END ************************************ + + // ************************************ Gamma & HDR ************************************ + FEPostProcess* GammaHDR = ENGINE.CreatePostProcess("GammaAndHDR", ViewportSize.x, ViewportSize.y); + FEShader* GammaHDRShader = RESOURCE_MANAGER.GetShader("3417497A5E0C0C2A07456E44"/*"FEGammaAndHDRShader"*/); + GammaHDR->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, GammaHDRShader)); + RENDERER.AddPostProcess(Result, GammaHDR); + // ************************************ Gamma & HDR END ************************************ + + // ************************************ FXAA *************************************** + FEPostProcess* FEFXAAEffect = ENGINE.CreatePostProcess("FE_FXAA", ViewportSize.x, ViewportSize.y); + FEShader* FEFXAAShader = RESOURCE_MANAGER.GetShader("1E69744A10604C2A1221426B"/*"FEFXAAShader"*/); + FEFXAAEffect->AddStage(new FEPostProcessStage(FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, FEFXAAShader)); + RENDERER.AddPostProcess(Result, FEFXAAEffect); + + //#fix for now after gamma correction I assume that texture output should be GL_RGB but in future it should be changeable. + Result->PostProcessEffects.back()->ReplaceOutTexture(0, RESOURCE_MANAGER.CreateTexture(GL_RGB, GL_RGB, ViewportSize.x, ViewportSize.y)); + // ************************************ FXAA END ************************************ + + // ************************************ DOF ************************************ + FEPostProcess* DOFEffect = ENGINE.CreatePostProcess("DOF", ViewportSize.x, ViewportSize.y); + FEShader* DOFShader = RESOURCE_MANAGER.GetShader("7800253C244442155D0F3C7B"/*"DOF"*/); + DOFEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, FE_POST_PROCESS_SCENE_DEPTH}, DOFShader)); + DOFEffect->Stages.back()->StageSpecificUniformValues.push_back(FEShaderUniformValue("FEBlurDirection", glm::vec2(0.0f, 1.0f))); + DOFEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0, FE_POST_PROCESS_SCENE_DEPTH}, DOFShader)); + DOFEffect->Stages.back()->StageSpecificUniformValues.push_back(FEShaderUniformValue("FEBlurDirection", glm::vec2(1.0f, 0.0f))); + + RENDERER.AddPostProcess(Result, DOFEffect); + // ************************************ DOF END ************************************ + + // ************************************ Chromatic Aberration ************************************ + FEPostProcess* ChromaticAberrationEffect = ENGINE.CreatePostProcess("chromaticAberration", ViewportSize.x, ViewportSize.y); + FEShader* ChromaticAberrationShader = RESOURCE_MANAGER.GetShader("9A41665B5E2B05321A332D09"/*"chromaticAberrationShader"*/); + ChromaticAberrationEffect->AddStage(new FEPostProcessStage(std::vector { FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0 }, ChromaticAberrationShader)); + RENDERER.AddPostProcess(Result, ChromaticAberrationEffect); + //#fix for now after gamma correction I assume that texture output should be GL_RGB but in future it should be changeable. + Result->PostProcessEffects.back()->ReplaceOutTexture(0, RESOURCE_MANAGER.CreateTexture(GL_RGB, GL_RGB, ViewportSize.x, ViewportSize.y)); + // ************************************ Chromatic Aberration END ************************************ + + return Result; +} + +FECameraRenderingData* FERenderer::GetCameraRenderingData(FEEntity* CameraEntity) +{ + if (CameraEntity == nullptr) + return nullptr; + + if (CameraRenderingDataMap.find(CameraEntity->GetObjectID()) != CameraRenderingDataMap.end()) + { + return CameraRenderingDataMap[CameraEntity->GetObjectID()]; + } + else + { + FECameraRenderingData* Result = CreateCameraRenderingData(CameraEntity); + if (Result != nullptr) + { + CameraRenderingDataMap[CameraEntity->GetObjectID()] = Result; + return CameraRenderingDataMap[CameraEntity->GetObjectID()]; + } + else + { + return nullptr; + } + } } -void FERenderer::Render(FEBasicCamera* CurrentCamera) +void FERenderer::ForceCameraRenderingDataUpdate(FEEntity* CameraEntity) { - LastRenderedResult = nullptr; + if (CameraEntity == nullptr) + return; + + if (CameraRenderingDataMap.find(CameraEntity->GetObjectID()) != CameraRenderingDataMap.end()) + { + delete CameraRenderingDataMap[CameraEntity->GetObjectID()]; + CameraRenderingDataMap.erase(CameraEntity->GetObjectID()); + } + + FECameraRenderingData* Result = CreateCameraRenderingData(CameraEntity); + CameraRenderingDataMap[CameraEntity->GetObjectID()] = Result; +} + +FETexture* FERenderer::GetCameraResult(FEEntity* CameraEntity) +{ + FETexture* Result = nullptr; + + if (CameraEntity == nullptr) + return Result; + + FECameraRenderingData* CameraRenderingData = GetCameraRenderingData(CameraEntity); + if (CameraRenderingData == nullptr) + return Result; + + if (CameraRenderingData->FinalScene != nullptr) + { + Result = CameraRenderingData->FinalScene; + } + else if (CameraRenderingData->SceneToTextureFB->GetColorAttachment() != nullptr) + { + Result = CameraRenderingData->SceneToTextureFB->GetColorAttachment(); + } + + return Result; +} + +void FERenderer::Render(FEScene* CurrentScene) +{ + if (CurrentScene == nullptr) + return; + + FEEntity* MainCameraEntity = CAMERA_SYSTEM.GetMainCamera(CurrentScene); + if (MainCameraEntity == nullptr) + return; + + CurrentCameraRenderingData = GetCameraRenderingData(MainCameraEntity); + if (CurrentCameraRenderingData == nullptr) + return; + + UpdateShadersForCamera(CurrentCameraRenderingData); + + FECameraComponent& CurrentCameraComponent = MainCameraEntity->GetComponent(); + FETransformComponent& CurrentCameraTransformComponent = MainCameraEntity->GetComponent(); + + FEViewport* CurrentViewport = CAMERA_SYSTEM.GetMainCameraViewport(CurrentScene); + if (CurrentViewport != nullptr) + { + glm::ivec2 ViewportPosition = glm::ivec2(CurrentViewport->GetX(), CurrentViewport->GetY()); + glm::ivec2 ViewportSize = glm::ivec2(CurrentViewport->GetWidth(), CurrentViewport->GetHeight()); + + MouseRay = GEOMETRY.CreateMouseRayToWorld(INPUT.GetMouseX(), INPUT.GetMouseY(), + CurrentCameraComponent.GetViewMatrix(), CurrentCameraComponent.GetProjectionMatrix(), + ViewportPosition, ViewportSize); + } if (bVRActive) return; - if (bSimplifiedRendering) + if (CurrentCameraComponent.GetRenderingPipeline() == FERenderingPipeline::Forward_Simplified) { - SimplifiedRender(CurrentCamera); + SimplifiedRender(CurrentScene); return; } - CurrentCamera->UpdateFrustumPlanes(); + CurrentCameraComponent.UpdateFrustumPlanes(); LastTestTime = TestTime; TestTime = 0.0f; @@ -655,22 +815,30 @@ void FERenderer::Render(FEBasicCamera* CurrentCamera) // there is only 1 directional light, sun. // and we need to set correct light position //#fix it should update view matrices for each cascade! - - auto LightIterator = OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].begin(); - while (LightIterator != OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].end()) - { - FEDirectionalLight* Light = reinterpret_cast(LightIterator->second); - if (Light->IsCastShadows()) + FEEntity* DirectionalLightEntity = nullptr; + std::vector< std::string> LightsIDList = CurrentScene->GetEntityIDListWithComponent(); + for (size_t i = 0; i < LightsIDList.size(); i++) + { + FEEntity* LightEntity = CurrentScene->GetEntity(LightsIDList[i]); + FETransformComponent& TransformComponent = LightEntity->GetComponent(); + FELightComponent& LightComponent = LightEntity->GetComponent(); + + if (LightComponent.GetType() != FE_DIRECTIONAL_LIGHT) + continue; + + if (LightComponent.IsCastShadows()) { - Light->UpdateCascades(CurrentCamera->Fov, CurrentCamera->AspectRatio, - CurrentCamera->NearPlane, CurrentCamera->FarPlane, - CurrentCamera->ViewMatrix, CurrentCamera->GetForward(), - CurrentCamera->GetRight(), CurrentCamera->GetUp()); + LIGHT_SYSTEM.UpdateCascades(LightEntity, CurrentCameraComponent.GetFOV(), CurrentCameraComponent.GetAspectRatio(), + CurrentCameraComponent.GetNearPlane(), CurrentCameraComponent.GetFarPlane(), + CurrentCameraComponent.GetViewMatrix(), CurrentCameraComponent.GetForward(), + CurrentCameraComponent.GetRight(), CurrentCameraComponent.GetUp()); } - LightIterator++; + + DirectionalLightEntity = LightEntity; + break; } - LoadUniformBlocks(); + LoadUniformBlocks(CurrentScene); // ********* GENERATE SHADOW MAPS ********* const bool PreviousState = bUseOcclusionCulling; @@ -686,152 +854,170 @@ void FERenderer::Render(FEBasicCamera* CurrentCamera) FEShader* ShaderInstancedPBR = RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/); FEShader* ShaderTerrain = RESOURCE_MANAGER.GetShader("5A3E4F5C13115856401F1D1C"/*"FETerrainShader"*/); - auto ItLight = OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].begin(); - while (ItLight != OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].end()) + // group group takes ownership of the Component_TYPE. + entt::basic_group GameModelGroup = CurrentScene->Registry.group(entt::get); + entt::basic_view PrefabInstancedView = CurrentScene->Registry.view(); + entt::basic_view TerrainView = CurrentScene->Registry.view(); + entt::basic_view VirtualUIView = CurrentScene->Registry.view(); + + for (std::string EntityID: LightsIDList) { - FEDirectionalLight* Light = reinterpret_cast(ItLight->second); - if (Light->IsCastShadows()) + FEEntity* LightEntity = CurrentScene->GetEntity(EntityID); + FETransformComponent& TransformComponent = LightEntity->GetComponent(); + FELightComponent& LightComponent = LightEntity->GetComponent(); + + if (LightComponent.GetType() != FE_DIRECTIONAL_LIGHT) + continue; + + if (LightComponent.IsCastShadows()) { - const float ShadowsBlurFactor = Light->GetShadowBlurFactor(); - ShaderPBR->UpdateParameterData("shadowBlurFactor", ShadowsBlurFactor); - ShaderInstancedPBR->UpdateParameterData("shadowBlurFactor", ShadowsBlurFactor); + const float ShadowsBlurFactor = LightComponent.GetShadowBlurFactor(); + ShaderPBR->UpdateUniformData("shadowBlurFactor", ShadowsBlurFactor); + ShaderInstancedPBR->UpdateUniformData("shadowBlurFactor", ShadowsBlurFactor); - const glm::vec3 OldCameraPosition = CurrentCamera->GetPosition(); - const glm::mat4 OldViewMatrix = CurrentCamera->GetViewMatrix(); - const glm::mat4 OldProjectionMatrix = CurrentCamera->GetProjectionMatrix(); + const glm::vec3 OldCameraPosition = CurrentCameraTransformComponent.GetPosition(FE_WORLD_SPACE); + const glm::mat4 OldViewMatrix = CurrentCameraComponent.GetViewMatrix(); + const glm::mat4 OldProjectionMatrix = CurrentCameraComponent.GetProjectionMatrix(); - for (size_t i = 0; i < static_cast(Light->ActiveCascades); i++) + for (size_t i = 0; i < static_cast(LightComponent.ActiveCascades); i++) { - // put camera to the position of light - CurrentCamera->ProjectionMatrix = Light->CascadeData[i].ProjectionMat; - CurrentCamera->ViewMatrix = Light->CascadeData[i].ViewMat; + if (LightComponent.CascadeData[i].FrameBuffer == nullptr) + { + LOG.Add("Function FERenderer::Render, LightComponent.CascadeData[i].FrameBuffer is nullptr!", "FE_LOG_RENDERING", FE_LOG_ERROR); + continue; + } + + // Put camera to the position of light. + CurrentCameraComponent.ProjectionMatrix = LightComponent.CascadeData[i].ProjectionMat; + CurrentCameraComponent.ViewMatrix = LightComponent.CascadeData[i].ViewMat; - FE_GL_ERROR(glViewport(0, 0, Light->CascadeData[i].FrameBuffer->GetWidth(), Light->CascadeData[i].FrameBuffer->GetHeight())); + SetGLViewport(0, 0, LightComponent.CascadeData[i].FrameBuffer->GetWidth(), LightComponent.CascadeData[i].FrameBuffer->GetHeight()); - UpdateGPUCullingFrustum(Light->CascadeData[i].Frustum, CurrentCamera->GetPosition()); + UpdateGPUCullingFrustum(MainCameraEntity); - Light->CascadeData[i].FrameBuffer->Bind(); + LightComponent.CascadeData[i].FrameBuffer->Bind(); FE_GL_ERROR(glClear(GL_DEPTH_BUFFER_BIT)); - auto ItTerrain = SCENE.TerrainMap.begin(); - while (ItTerrain != SCENE.TerrainMap.end()) + for (auto [EnTTEntity, TerrainComponent, TransformComponent] : TerrainView.each()) { - auto terrain = ItTerrain->second; - if (!terrain->IsCastingShadows() || !terrain->IsVisible()) - { - ItTerrain++; + FEEntity* Entity = CurrentScene->GetEntityByEnTT(EnTTEntity); + if (Entity == nullptr) continue; - } - - terrain->Shader = RESOURCE_MANAGER.GetShader("50064D3C4D0B537F0846274F"/*"FESMTerrainShader"*/); - RenderTerrain(terrain, CurrentCamera); - terrain->Shader = RESOURCE_MANAGER.GetShader("5A3E4F5C13115856401F1D1C"/*"FETerrainShader"*/); - ItTerrain++; + + if (!TerrainComponent.IsCastingShadows() || !TerrainComponent.IsVisible()) + continue; + + TerrainComponent.Shader = RESOURCE_MANAGER.GetShader("50064D3C4D0B537F0846274F"/*"FESMTerrainShader"*/); + RenderTerrainComponent(Entity, MainCameraEntity); + TerrainComponent.Shader = RESOURCE_MANAGER.GetShader("5A3E4F5C13115856401F1D1C"/*"FETerrainShader"*/); } - auto it = SCENE.EntityMap.begin(); - while (it != SCENE.EntityMap.end()) + for (entt::entity EnTTEntity : GameModelGroup) { - const auto Entity = it->second; - if (!Entity->IsCastShadows() || !Entity->IsVisible() || Entity->Prefab == nullptr) - { - it++; + auto& [GameModelComponent, TransformComponent] = GameModelGroup.get(EnTTEntity); + + if (!GameModelComponent.IsCastShadows() || !GameModelComponent.IsVisible()) + continue; + + FEEntity* Entity = CurrentScene->GetEntityByEnTT(EnTTEntity); + if (GameModelComponent.GetGameModel() == nullptr) + continue; + FEMaterial* OriginalMaterial = GameModelComponent.GetGameModel()->Material; + if (OriginalMaterial == nullptr) continue; + + FEMaterial* ShadowMapMaterialToUse = !Entity->HasComponent() ? ShadowMapMaterial : ShadowMapMaterialInstanced; + GameModelComponent.GetGameModel()->Material = ShadowMapMaterialToUse; + ShadowMapMaterialToUse->SetAlbedoMap(OriginalMaterial->GetAlbedoMap()); + + if (OriginalMaterial->GetAlbedoMap(1) != nullptr) + { + ShadowMapMaterialToUse->SetAlbedoMap(OriginalMaterial->GetAlbedoMap(1), 1); + ShadowMapMaterialToUse->GetAlbedoMap(1)->Bind(1); } - if (Entity->GetType() == FE_ENTITY) + if (!Entity->HasComponent()) { - for (size_t j = 0; j < Entity->Prefab->Components.size(); j++) - { - FEMaterial* OriginalMaterial = Entity->Prefab->Components[j]->GameModel->Material; - Entity->Prefab->Components[j]->GameModel->Material = ShadowMapMaterial; - ShadowMapMaterial->SetAlbedoMap(OriginalMaterial->GetAlbedoMap()); - // if material have submaterial - if (OriginalMaterial->GetAlbedoMap(1) != nullptr) - { - ShadowMapMaterial->SetAlbedoMap(OriginalMaterial->GetAlbedoMap(1), 1); - ShadowMapMaterial->GetAlbedoMap(1)->Bind(1); - } - - RenderEntity(Entity, CurrentCamera, false, static_cast(j)); - - Entity->Prefab->Components[j]->GameModel->Material = OriginalMaterial; - for (size_t k = 0; k < ShadowMapMaterial->Textures.size(); k++) - { - ShadowMapMaterial->Textures[k] = nullptr; - ShadowMapMaterial->TextureBindings[k] = -1; - - ShadowMapMaterialInstanced->Textures[k] = nullptr; - ShadowMapMaterialInstanced->TextureBindings[k] = -1; - } - } + RenderGameModelComponent(Entity, MainCameraEntity, false); } - else if (Entity->GetType() == FE_ENTITY_INSTANCED) + else if (Entity->HasComponent()) { - std::vector OriginalMaterials; - FEEntityInstanced* CurrentEntity = reinterpret_cast(Entity); - for (size_t j = 0; j < CurrentEntity->Prefab->Components.size(); j++) + RenderGameModelComponentWithInstanced(Entity, MainCameraEntity, true, false); + } + + GameModelComponent.GetGameModel()->Material = OriginalMaterial; + for (size_t k = 0; k < ShadowMapMaterial->Textures.size(); k++) + { + ShadowMapMaterialToUse->Textures[k] = nullptr; + ShadowMapMaterialToUse->TextureBindings[k] = -1; + } + } + + for (auto [EnTTEntity, InstancedComponent, PrefabInstanceComponent] : PrefabInstancedView.each()) + { + FEEntity* Entity = CurrentScene->GetEntityByEnTT(EnTTEntity); + FEMaterial* OriginalMaterial; + + for (size_t i = 0; i < InstancedComponent.InstancedElementsData.size(); i++) + { + FEEntity* EntityWithGameModel = INSTANCED_RENDERING_SYSTEM.GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[i]->EntityIDWithGameModelComponent); + if (EntityWithGameModel == nullptr) + continue; + + FEGameModelComponent& GameModelComponent = EntityWithGameModel->GetComponent(); + + OriginalMaterial = GameModelComponent.GetGameModel()->Material; + FEMaterial* ShadowMapMaterialToUse = ShadowMapMaterialInstanced; + GameModelComponent.GetGameModel()->Material = ShadowMapMaterialToUse; + ShadowMapMaterialToUse->SetAlbedoMap(OriginalMaterial->GetAlbedoMap()); + + if (OriginalMaterial->GetAlbedoMap(1) != nullptr) { - OriginalMaterials.push_back(CurrentEntity->Prefab->Components[j]->GameModel->Material); - - CurrentEntity->Prefab->Components[j]->GameModel->Material = ShadowMapMaterialInstanced; - ShadowMapMaterialInstanced->SetAlbedoMap(OriginalMaterials.back()->GetAlbedoMap()); - // if material have submaterial - if (OriginalMaterials.back()->GetAlbedoMap(1) != nullptr) - { - ShadowMapMaterialInstanced->SetAlbedoMap(OriginalMaterials.back()->GetAlbedoMap(1), 1); - ShadowMapMaterialInstanced->GetAlbedoMap(1)->Bind(1); - } - - RenderEntityInstanced(CurrentEntity, CurrentCamera, Light->CascadeData[i].Frustum, true, false, static_cast(j)); - - Entity->Prefab->Components[j]->GameModel->Material = OriginalMaterials[j]; - for (size_t k = 0; k < ShadowMapMaterial->Textures.size(); k++) - { - ShadowMapMaterial->Textures[k] = nullptr; - ShadowMapMaterial->TextureBindings[k] = -1; - - ShadowMapMaterialInstanced->Textures[k] = nullptr; - ShadowMapMaterialInstanced->TextureBindings[k] = -1; - } + ShadowMapMaterialToUse->SetAlbedoMap(OriginalMaterial->GetAlbedoMap(1), 1); + ShadowMapMaterialToUse->GetAlbedoMap(1)->Bind(1); } - } - it++; + RenderGameModelComponentWithInstanced(Entity, MainCameraEntity, true, false, i); + + GameModelComponent.GetGameModel()->Material = OriginalMaterial; + for (size_t k = 0; k < ShadowMapMaterial->Textures.size(); k++) + { + ShadowMapMaterialToUse->Textures[k] = nullptr; + ShadowMapMaterialToUse->TextureBindings[k] = -1; + } + } } - Light->CascadeData[i].FrameBuffer->UnBind(); + LightComponent.CascadeData[i].FrameBuffer->UnBind(); switch (i) { - case 0: CSM0 = Light->CascadeData[i].FrameBuffer->GetDepthAttachment(); + case 0: CSM0 = LightComponent.CascadeData[i].FrameBuffer->GetDepthAttachment(); break; - case 1: CSM1 = Light->CascadeData[i].FrameBuffer->GetDepthAttachment(); + case 1: CSM1 = LightComponent.CascadeData[i].FrameBuffer->GetDepthAttachment(); break; - case 2: CSM2 = Light->CascadeData[i].FrameBuffer->GetDepthAttachment(); + case 2: CSM2 = LightComponent.CascadeData[i].FrameBuffer->GetDepthAttachment(); break; - case 3: CSM3 = Light->CascadeData[i].FrameBuffer->GetDepthAttachment(); + case 3: CSM3 = LightComponent.CascadeData[i].FrameBuffer->GetDepthAttachment(); break; default: break; } } - CurrentCamera->SetPosition(OldCameraPosition); - CurrentCamera->ViewMatrix = OldViewMatrix; - CurrentCamera->ProjectionMatrix = OldProjectionMatrix; + CurrentCameraTransformComponent.SetPosition(OldCameraPosition, FE_WORLD_SPACE); + CurrentCameraComponent.ViewMatrix = OldViewMatrix; + CurrentCameraComponent.ProjectionMatrix = OldProjectionMatrix; - FE_GL_ERROR(glViewport(0, 0, SceneToTextureFB->GetWidth(), SceneToTextureFB->GetHeight())); + SetGLViewport(0, 0, CurrentCameraRenderingData->SceneToTextureFB->GetWidth(), CurrentCameraRenderingData->SceneToTextureFB->GetHeight()); break; } - - ItLight++; } bUseOcclusionCulling = PreviousState; // ********* GENERATE SHADOW MAPS END ********* // in current version only shadows from one directional light is supported. - if (!OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].empty()) + if (DirectionalLightEntity != nullptr) { if (CSM0) CSM0->Bind(FE_CSM_UNIT); if (CSM1) CSM1->Bind(FE_CSM_UNIT + 1); @@ -841,87 +1027,106 @@ void FERenderer::Render(FEBasicCamera* CurrentCamera) // ********* RENDER SCENE ********* - GBuffer->GFrameBuffer->Bind(); + SetGLViewport(0, 0, CurrentCameraRenderingData->SceneToTextureFB->GetWidth(), CurrentCameraRenderingData->SceneToTextureFB->GetHeight()); + CurrentCameraRenderingData->GBuffer->GFrameBuffer->Bind(); - const unsigned int attachments[6] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 }; - glDrawBuffers(6, attachments); + const unsigned int FrameBufferColorAttachments[7] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5, GL_COLOR_ATTACHMENT6 }; + glDrawBuffers(7, FrameBufferColorAttachments); - FE_GL_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + if (CurrentCameraComponent.IsClearColorEnabled()) + { + glm::vec4 ClearColor = CurrentCameraComponent.GetClearColor(); + glClearColor(ClearColor.x, ClearColor.y, ClearColor.z, ClearColor.w); + FE_GL_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + } - UpdateGPUCullingFrustum(CurrentCamera->Frustum, CurrentCamera->GetPosition()); + UpdateGPUCullingFrustum(MainCameraEntity); - auto EntityIterator = SCENE.EntityMap.begin(); - while (EntityIterator != SCENE.EntityMap.end()) + for (entt::entity EnTTEntity : GameModelGroup) { - auto entity = EntityIterator->second; + auto& [GameModelComponent, TransformComponent] = GameModelGroup.get(EnTTEntity); + + if (!GameModelComponent.IsVisible() || !GameModelComponent.IsPostprocessApplied()) + continue; - if (entity->IsVisible() && entity->IsPostprocessApplied()) + FEEntity* Entity = CurrentScene->GetEntityByEnTT(EnTTEntity); + if (!Entity->HasComponent()) { - if (entity->GetType() == FE_ENTITY) - { - ForceShader(RESOURCE_MANAGER.GetShader("670B01496E202658377A4576"/*"FEPBRGBufferShader"*/)); - RenderEntity(entity, CurrentCamera); - } - else if (entity->GetType() == FE_ENTITY_INSTANCED) - { - ForceShader(RESOURCE_MANAGER.GetShader("613830232E12602D6A1D2C17"/*"FEPBRInstancedGBufferShader"*/)); - RenderEntityInstanced(reinterpret_cast(entity), CurrentCamera, CurrentCamera->GetFrustumPlanes(), false); - } + ForceShader(RESOURCE_MANAGER.GetShader("670B01496E202658377A4576"/*"FEPBRGBufferShader"*/)); + RenderGameModelComponent(Entity, MainCameraEntity); + } + else if (Entity->HasComponent()) + { + + ForceShader(RESOURCE_MANAGER.GetShader("613830232E12602D6A1D2C17"/*"FEPBRInstancedGBufferShader"*/)); + RenderGameModelComponentWithInstanced(Entity, MainCameraEntity); } + } + + for (auto [EnTTEntity, InstancedComponent, PrefabInstanceComponent] : PrefabInstancedView.each()) + { + ForceShader(RESOURCE_MANAGER.GetShader("613830232E12602D6A1D2C17"/*"FEPBRInstancedGBufferShader"*/)); + FEEntity* Entity = CurrentScene->GetEntityByEnTT(EnTTEntity); - EntityIterator++; + for (size_t i = 0; i < InstancedComponent.InstancedElementsData.size(); i++) + { + RenderGameModelComponentWithInstanced(Entity, nullptr, false, false, i); + } } - // It is not renderer work to update interaction ray. - // It should be done in the input update. - auto VirtualUIIterator = SCENE.VirtualUIContextMap.begin(); - while (VirtualUIIterator != SCENE.VirtualUIContextMap.end()) + for (auto [EnTTEntity, VirtualUIComponent, TransformComponent] : VirtualUIView.each()) { - auto VirtualUIContext = VirtualUIIterator->second; - if (VirtualUIContext->bMouseMovePassThrough) - VirtualUIContext->UpdateInteractionRay(CurrentCamera->GetPosition(), RENDERER.MouseRay); + FEEntity* Entity = CurrentScene->GetEntityByEnTT(EnTTEntity); + if (Entity == nullptr) + continue; - VirtualUIIterator++; + //if (!VirtualUIComponent.IsVisible()) + // continue; + + VIRTUAL_UI_SYSTEM.RenderVirtualUIComponent(Entity); } - auto ItTerrain = SCENE.TerrainMap.begin(); - while (ItTerrain != SCENE.TerrainMap.end()) + for (auto [EnTTEntity, TerrainComponent, TransformComponent] : TerrainView.each()) { - auto terrain = ItTerrain->second; - if (terrain->IsVisible()) - RenderTerrain(terrain, CurrentCamera); + FEEntity* Entity = CurrentScene->GetEntityByEnTT(EnTTEntity); + if (Entity == nullptr) + continue; + + if (!TerrainComponent.IsVisible()) + continue; - ItTerrain++; + RenderTerrainComponent(Entity, MainCameraEntity); } - GBuffer->GFrameBuffer->UnBind(); + CurrentCameraRenderingData->GBuffer->GFrameBuffer->UnBind(); ForceShader(nullptr); - GBuffer->Albedo->Bind(0); - GBuffer->Normals->Bind(1); - GBuffer->MaterialProperties->Bind(2); - GBuffer->Positions->Bind(3); - GBuffer->ShaderProperties->Bind(4); + CurrentCameraRenderingData->GBuffer->Albedo->Bind(0); + CurrentCameraRenderingData->GBuffer->Normals->Bind(1); + CurrentCameraRenderingData->GBuffer->MaterialProperties->Bind(2); + CurrentCameraRenderingData->GBuffer->Positions->Bind(3); + CurrentCameraRenderingData->GBuffer->ShaderProperties->Bind(4); // ************************************ SSAO ************************************ - UpdateSSAO(CurrentCamera); + UpdateSSAO(); // ************************************ SSAO END ************************************ // ************************************ COPYING DEPTH BUFFER ************************************ - glBindFramebuffer(GL_READ_FRAMEBUFFER, GBuffer->GFrameBuffer->FBO); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, SceneToTextureFB->FBO); // write to default framebuffer - glBlitFramebuffer(0, 0, SceneToTextureFB->GetWidth(), SceneToTextureFB->GetHeight(), - 0, 0, SceneToTextureFB->GetWidth(), SceneToTextureFB->GetHeight(), + glBindFramebuffer(GL_READ_FRAMEBUFFER, CurrentCameraRenderingData->GBuffer->GFrameBuffer->FBO); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CurrentCameraRenderingData->SceneToTextureFB->FBO); // write to default framebuffer + glBlitFramebuffer(0, 0, CurrentCameraRenderingData->SceneToTextureFB->GetWidth(), CurrentCameraRenderingData->SceneToTextureFB->GetHeight(), + 0, 0, CurrentCameraRenderingData->SceneToTextureFB->GetWidth(), CurrentCameraRenderingData->SceneToTextureFB->GetHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST); // ************************************ COPYING DEPTH BUFFER END ************************************ - GBuffer->Albedo->Bind(0); - GBuffer->Normals->Bind(1); - GBuffer->MaterialProperties->Bind(2); - GBuffer->Positions->Bind(3); - GBuffer->ShaderProperties->Bind(4); - SSAO->FB->GetColorAttachment()->Bind(5); - SceneToTextureFB->Bind(); + CurrentCameraRenderingData->GBuffer->Albedo->Bind(0); + CurrentCameraRenderingData->GBuffer->Normals->Bind(1); + CurrentCameraRenderingData->GBuffer->MaterialProperties->Bind(2); + CurrentCameraRenderingData->GBuffer->Positions->Bind(3); + CurrentCameraRenderingData->GBuffer->ShaderProperties->Bind(4); + CurrentCameraRenderingData->SSAO->FB->GetColorAttachment()->Bind(5); + CurrentCameraRenderingData->GBuffer->MotionVectors->Bind(6); + CurrentCameraRenderingData->SceneToTextureFB->Bind(); const unsigned int attachments_[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; glDrawBuffers(3, attachments_); @@ -931,9 +1136,9 @@ void FERenderer::Render(FEBasicCamera* CurrentCamera) FEShader* FinalSceneShader = RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/); FinalSceneShader->Start(); - FinalSceneShader->UpdateParameterData("SSAOActive", SSAO->bActive ? 1.0f : 0.0f); - LoadStandardParams(FinalSceneShader, CurrentCamera, true); - FinalSceneShader->LoadDataToGPU(); + FinalSceneShader->UpdateUniformData("SSAOActive", CurrentCameraComponent.IsSSAOEnabled() ? 1.0f : 0.0f); + LoadStandardUniforms(FinalSceneShader, true, MainCameraEntity); + FinalSceneShader->LoadUniformsDataToGPU(); FE_GL_ERROR(glBindVertexArray(RESOURCE_MANAGER.GetMesh("1Y251E6E6T78013635793156"/*"plane"*/)->GetVaoID())); FE_GL_ERROR(glEnableVertexAttribArray(0)); @@ -941,11 +1146,12 @@ void FERenderer::Render(FEBasicCamera* CurrentCamera) FE_GL_ERROR(glDisableVertexAttribArray(0)); FE_GL_ERROR(glBindVertexArray(0)); - GBuffer->Albedo->UnBind(); - GBuffer->Normals->UnBind(); - GBuffer->MaterialProperties->UnBind(); - GBuffer->Positions->UnBind(); - GBuffer->ShaderProperties->UnBind(); + CurrentCameraRenderingData->GBuffer->Albedo->UnBind(); + CurrentCameraRenderingData->GBuffer->Normals->UnBind(); + CurrentCameraRenderingData->GBuffer->MaterialProperties->UnBind(); + CurrentCameraRenderingData->GBuffer->Positions->UnBind(); + CurrentCameraRenderingData->GBuffer->ShaderProperties->UnBind(); + CurrentCameraRenderingData->GBuffer->MotionVectors->UnBind(); FinalSceneShader->Stop(); @@ -961,10 +1167,10 @@ void FERenderer::Render(FEBasicCamera* CurrentCamera) FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, 0)); InstancedLineShader->Start(); - InstancedLineShader->UpdateParameterData("FEProjectionMatrix", CurrentCamera->GetProjectionMatrix()); - InstancedLineShader->UpdateParameterData("FEViewMatrix", CurrentCamera->GetViewMatrix()); - InstancedLineShader->UpdateParameterData("resolution", glm::vec2(SceneToTextureFB->GetWidth(), SceneToTextureFB->GetHeight())); - InstancedLineShader->LoadDataToGPU(); + InstancedLineShader->UpdateUniformData("FEProjectionMatrix", CurrentCameraComponent.GetProjectionMatrix()); + InstancedLineShader->UpdateUniformData("FEViewMatrix", CurrentCameraComponent.GetViewMatrix()); + InstancedLineShader->UpdateUniformData("resolution", glm::vec2(CurrentCameraRenderingData->SceneToTextureFB->GetWidth(), CurrentCameraRenderingData->SceneToTextureFB->GetHeight())); + InstancedLineShader->LoadUniformsDataToGPU(); FE_GL_ERROR(glBindVertexArray(InstancedLineVAO)); FE_GL_ERROR(glEnableVertexAttribArray(0)); @@ -986,56 +1192,76 @@ void FERenderer::Render(FEBasicCamera* CurrentCamera) // ********* RENDER INSTANCED LINE END ********* // ********* RENDER SKY ********* - if (IsSkyEnabled()) - RenderEntity(SkyDome, CurrentCamera); + entt::basic_view SkyDomeView = CurrentScene->Registry.view(); + for (auto [EnTTEntity, SkyDomeComponent, TransformComponent] : SkyDomeView.each()) + { + FEEntity* CurrentEntity = CurrentScene->GetEntityByEnTT(EnTTEntity); + if (CurrentEntity == nullptr) + continue; - SceneToTextureFB->UnBind(); + if (!SKY_DOME_SYSTEM.IsEnabled()) + { + CurrentEntity->GetComponent().SetVisibility(false); + break; + } + + CurrentEntity->GetComponent().SetVisibility(true); + RenderGameModelComponent(CurrentEntity, MainCameraEntity); + CurrentEntity->GetComponent().SetVisibility(false); + // Only one sky dome is supported. + break; + } // ********* RENDER SCENE END ********* + CurrentCameraRenderingData->SceneToTextureFB->UnBind(); //Generate the mipmaps of colorAttachment - SceneToTextureFB->GetColorAttachment()->Bind(); + CurrentCameraRenderingData->SceneToTextureFB->GetColorAttachment()->Bind(); glGenerateMipmap(GL_TEXTURE_2D); + + + // ********* Upscale rendering result if needed ************* + + // Nothing here for now. + + // ********* Upscale rendering result if needed END ********* // ********* POST_PROCESS EFFECTS ********* // Because we render post process effects with screen quad // we will turn off write to depth buffer in order to get clear DB to be able to render additional objects glDepthMask(GL_FALSE); - FinalScene = SceneToTextureFB->GetColorAttachment(); - FETexture* PrevStageTex = SceneToTextureFB->GetColorAttachment(); - for (size_t i = 0; i < PostProcessEffects.size(); i++) + FETexture* PreviousStageTexture = CurrentCameraRenderingData->SceneToTextureFB->GetColorAttachment(); + for (size_t i = 0; i < CurrentCameraRenderingData->PostProcessEffects.size(); i++) { - FEPostProcess& Effect = *PostProcessEffects[i]; + FEPostProcess& Effect = *CurrentCameraRenderingData->PostProcessEffects[i]; for (size_t j = 0; j < Effect.Stages.size(); j++) { Effect.Stages[j]->Shader->Start(); - LoadStandardParams(Effect.Stages[j]->Shader, CurrentCamera, nullptr, nullptr); - for (size_t k = 0; k < Effect.Stages[j]->StageSpecificUniforms.size(); k++) + LoadStandardUniforms(Effect.Stages[j]->Shader, nullptr, nullptr, MainCameraEntity); + for (size_t k = 0; k < Effect.Stages[j]->StageSpecificUniformValues.size(); k++) { - FEShaderParam* Param = Effect.Stages[j]->Shader->GetParameter(Effect.Stages[j]->StageSpecificUniforms[k].GetName()); - if (Param != nullptr) - { - Param->Data = Effect.Stages[j]->StageSpecificUniforms[k].Data; - } + FEShaderUniform* CurrentUniform = Effect.Stages[j]->Shader->GetUniform(Effect.Stages[j]->StageSpecificUniformValues[k].GetName()); + if (CurrentUniform != nullptr) + CurrentUniform->CurrentValue = Effect.Stages[j]->StageSpecificUniformValues[k]; } - Effect.Stages[j]->Shader->LoadDataToGPU(); + Effect.Stages[j]->Shader->LoadUniformsDataToGPU(); for (size_t k = 0; k < Effect.Stages[j]->InTextureSource.size(); k++) { if (Effect.Stages[j]->InTextureSource[k] == FE_POST_PROCESS_PREVIOUS_STAGE_RESULT0) { - Effect.Stages[j]->InTexture[k] = PrevStageTex; + Effect.Stages[j]->InTexture[k] = PreviousStageTexture; Effect.Stages[j]->InTexture[k]->Bind(static_cast(k)); } else if (Effect.Stages[j]->InTextureSource[k] == FE_POST_PROCESS_SCENE_HDR_COLOR) { - Effect.Stages[j]->InTexture[k] = SceneToTextureFB->GetColorAttachment(); + Effect.Stages[j]->InTexture[k] = CurrentCameraRenderingData->SceneToTextureFB->GetColorAttachment(); Effect.Stages[j]->InTexture[k]->Bind(static_cast(k)); } else if (Effect.Stages[j]->InTextureSource[k] == FE_POST_PROCESS_SCENE_DEPTH) { - Effect.Stages[j]->InTexture[k] = SceneToTextureFB->GetDepthAttachment(); + Effect.Stages[j]->InTexture[k] = CurrentCameraRenderingData->SceneToTextureFB->GetDepthAttachment(); Effect.Stages[j]->InTexture[k]->Bind(static_cast(k)); } else if (Effect.Stages[j]->InTextureSource[k] == FE_POST_PROCESS_OWN_TEXTURE) @@ -1046,13 +1272,13 @@ void FERenderer::Render(FEBasicCamera* CurrentCamera) FETexture* OrdinaryColorAttachment = Effect.IntermediateFramebuffer->GetColorAttachment(); Effect.IntermediateFramebuffer->SetColorAttachment(Effect.Stages[j]->OutTexture); - if (Effect.Stages[j]->OutTexture->Width != SceneToTextureFB->GetWidth()) + if (Effect.Stages[j]->OutTexture->Width != CurrentCameraRenderingData->SceneToTextureFB->GetWidth()) { - FE_GL_ERROR(glViewport(0, 0, Effect.Stages[j]->OutTexture->Width, Effect.Stages[j]->OutTexture->Height)); + SetGLViewport(0, 0, Effect.Stages[j]->OutTexture->Width, Effect.Stages[j]->OutTexture->Height); } else { - FE_GL_ERROR(glViewport(0, 0, SceneToTextureFB->GetWidth(), SceneToTextureFB->GetHeight())); + SetGLViewport(0, 0, CurrentCameraRenderingData->SceneToTextureFB->GetWidth(), CurrentCameraRenderingData->SceneToTextureFB->GetHeight()); } Effect.IntermediateFramebuffer->Bind(); @@ -1072,18 +1298,25 @@ void FERenderer::Render(FEBasicCamera* CurrentCamera) } Effect.Stages[j]->Shader->Stop(); - PrevStageTex = Effect.Stages[j]->OutTexture; + PreviousStageTexture = Effect.Stages[j]->OutTexture; } } - for (int i = static_cast(PostProcessEffects.size() - 1); i >= 0; i--) + // Rendering the last effect to the default screen buffer. + for (int i = static_cast(CurrentCameraRenderingData->PostProcessEffects.size() - 1); i >= 0; i--) { - FEPostProcess& Effect = *PostProcessEffects[i]; + FEPostProcess& Effect = *CurrentCameraRenderingData->PostProcessEffects[i]; if (Effect.bActive) { - Effect.RenderResult(); - FinalScene = Effect.Stages.back()->OutTexture; + // That is where final rendering to screen is happening if Viewport is default. + if (CurrentCameraRenderingData->CameraEntity->GetComponent().Viewport == ENGINE.GetDefaultViewport()) + Effect.RenderResult(); + // TO_DO: With introduction of multiple scenes\cameras, this code probably should be changed. + // Temporary solution, now Engine will not render to default screen buffer, user would be responsible for it. + // Maybe use ENGINE.GetRenderTargetMode() to determine if it should render to default screen buffer. + //Effect.RenderResult(); + CurrentCameraRenderingData->FinalScene = Effect.Stages.back()->OutTexture; break; } } @@ -1092,44 +1325,34 @@ void FERenderer::Render(FEBasicCamera* CurrentCamera) // ********* SCREEN SPACE EFFECTS END ********* // ********* ENTITIES THAT WILL NOT BE IMPACTED BY POST PROCESS. MAINLY FOR UI ********* - FETexture* OriginalColorAttachment = SceneToTextureFB->GetColorAttachment(); - SceneToTextureFB->SetColorAttachment(FinalScene); - - SceneToTextureFB->Bind(); + FETexture* OriginalColorAttachment = CurrentCameraRenderingData->SceneToTextureFB->GetColorAttachment(); + CurrentCameraRenderingData->SceneToTextureFB->SetColorAttachment(CurrentCameraRenderingData->FinalScene); + CurrentCameraRenderingData->SceneToTextureFB->Bind(); - EntityIterator = SCENE.EntityMap.begin(); - while (EntityIterator != SCENE.EntityMap.end()) + for (entt::entity EnTTEntity : GameModelGroup) { - auto Entity = EntityIterator->second; + auto& [GameModelComponent, TransformComponent] = GameModelGroup.get(EnTTEntity); - if (Entity->IsVisible() && !Entity->IsPostprocessApplied()) + if (!GameModelComponent.IsVisible() || GameModelComponent.IsPostprocessApplied()) + continue; + + FEEntity* Entity = CurrentScene->GetEntityByEnTT(EnTTEntity); + if (!Entity->HasComponent()) { - if (Entity->GetType() == FE_ENTITY) - { - RenderEntity(Entity, CurrentCamera); - } - else if (Entity->GetType() == FE_ENTITY_INSTANCED) - { - } + RenderGameModelComponent(Entity, MainCameraEntity); + } + else if (Entity->HasComponent()) + { + RenderGameModelComponentWithInstanced(Entity, MainCameraEntity); } - - EntityIterator++; } - SceneToTextureFB->UnBind(); - SceneToTextureFB->SetColorAttachment(OriginalColorAttachment); + CurrentCameraRenderingData->SceneToTextureFB->UnBind(); + CurrentCameraRenderingData->SceneToTextureFB->SetColorAttachment(OriginalColorAttachment); // ********* ENTITIES THAT WILL NOT BE IMPACTED BY POST PROCESS. MAINLY FOR UI END ********* // **************************** TERRAIN EDITOR TOOLS **************************** - ItTerrain = SCENE.TerrainMap.begin(); - while (ItTerrain != SCENE.TerrainMap.end()) - { - auto terrain = ItTerrain->second; - if (terrain->IsVisible()) - UpdateTerrainBrush(terrain); - - ItTerrain++; - } + TERRAIN_SYSTEM.UpdateBrush(CurrentCameraTransformComponent.GetPosition(FE_WORLD_SPACE), MouseRay); // **************************** TERRAIN EDITOR TOOLS END **************************** LineCounter = 0; @@ -1138,336 +1361,342 @@ void FERenderer::Render(FEBasicCamera* CurrentCamera) #ifdef USE_OCCLUSION_CULLING ComputeTextureCopy->Start(); - ComputeTextureCopy->UpdateParameterData("textureSize", glm::vec2(RENDERER.DepthPyramid->GetWidth(), RENDERER.DepthPyramid->GetHeight())); - ComputeTextureCopy->LoadDataToGPU(); + ComputeTextureCopy->UpdateUniformData("textureSize", glm::vec2(CurrentCameraRenderingData->DepthPyramid->GetWidth(), CurrentCameraRenderingData->DepthPyramid->GetHeight())); + ComputeTextureCopy->LoadUniformsDataToGPU(); - RENDERER.SceneToTextureFB->GetDepthAttachment()->Bind(0); - glBindImageTexture(1, RENDERER.DepthPyramid->GetTextureID(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + CurrentCameraRenderingData->SceneToTextureFB->GetDepthAttachment()->Bind(0); + glBindImageTexture(1, CurrentCameraRenderingData->DepthPyramid->GetTextureID(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); - ComputeTextureCopy->Dispatch(static_cast(ceil(float(RENDERER.DepthPyramid->GetWidth()) / 32.0f)), static_cast(ceil(float(RENDERER.DepthPyramid->GetHeight()) / 32.0f)), 1); + ComputeTextureCopy->Dispatch(static_cast(ceil(float(CurrentCameraRenderingData->DepthPyramid->GetWidth()) / 32.0f)), static_cast(ceil(float(CurrentCameraRenderingData->DepthPyramid->GetHeight()) / 32.0f)), 1); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); - const size_t MipCount = static_cast(floor(log2(std::max(RENDERER.DepthPyramid->GetWidth(), RENDERER.DepthPyramid->GetHeight()))) + 1); + const size_t MipCount = static_cast(floor(log2(std::max(CurrentCameraRenderingData->DepthPyramid->GetWidth(), CurrentCameraRenderingData->DepthPyramid->GetHeight()))) + 1); for (size_t i = 0; i < MipCount; i++) { const float DownScale = static_cast(pow(2.0f, i)); ComputeDepthPyramidDownSample->Start(); - ComputeDepthPyramidDownSample->UpdateParameterData("textureSize", glm::vec2(RENDERER.DepthPyramid->GetWidth() / DownScale, RENDERER.DepthPyramid->GetHeight() / DownScale)); - ComputeDepthPyramidDownSample->LoadDataToGPU(); - glBindImageTexture(0, RENDERER.DepthPyramid->GetTextureID(), static_cast(i), GL_FALSE, 0, GL_READ_ONLY, GL_R32F); - glBindImageTexture(1, RENDERER.DepthPyramid->GetTextureID(), static_cast(i + 1), GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); + ComputeDepthPyramidDownSample->UpdateUniformData("textureSize", glm::vec2(CurrentCameraRenderingData->DepthPyramid->GetWidth() / DownScale, CurrentCameraRenderingData->DepthPyramid->GetHeight() / DownScale)); + ComputeDepthPyramidDownSample->LoadUniformsDataToGPU(); + glBindImageTexture(0, CurrentCameraRenderingData->DepthPyramid->GetTextureID(), static_cast(i), GL_FALSE, 0, GL_READ_ONLY, GL_R32F); + glBindImageTexture(1, CurrentCameraRenderingData->DepthPyramid->GetTextureID(), static_cast(i + 1), GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); - ComputeDepthPyramidDownSample->Dispatch(static_cast(ceil(float(RENDERER.DepthPyramid->GetWidth() / DownScale) / 32.0f)), static_cast(ceil(float(RENDERER.DepthPyramid->GetHeight() / DownScale) / 32.0f)), 1); + ComputeDepthPyramidDownSample->Dispatch(static_cast(ceil(float(CurrentCameraRenderingData->DepthPyramid->GetWidth() / DownScale) / 32.0f)), static_cast(ceil(float(CurrentCameraRenderingData->DepthPyramid->GetHeight() / DownScale) / 32.0f)), 1); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); } #endif // USE_OCCLUSION_CULLING // **************************** DEPTH PYRAMID END **************************** + + CurrentCameraRenderingData = nullptr; } -FEPostProcess* FERenderer::GetPostProcessEffect(const std::string ID) +void FERenderer::SaveScreenshot(std::string FileName, FEScene* SceneToWorkWith) { - for (size_t i = 0; i < PostProcessEffects.size(); i++) + if (SceneToWorkWith == nullptr) { - if (PostProcessEffects[i]->GetObjectID() == ID) - return PostProcessEffects[i]; + LOG.Add("Scene is nullptr in FERenderer::SaveScreenshot", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; } - return nullptr; -} + FEEntity* MainCameraEntity = CAMERA_SYSTEM.GetMainCamera(SceneToWorkWith); + if (MainCameraEntity == nullptr) + { + LOG.Add("Main camera entity for given scene is nullptr in FERenderer::SaveScreenshot", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } -std::vector FERenderer::GetPostProcessList() -{ - std::vector result; - for (size_t i = 0; i < PostProcessEffects.size(); i++) - result.push_back(PostProcessEffects[i]->GetObjectID()); - - return result; -} + FECameraRenderingData* CameraRenderingData = GetCameraRenderingData(MainCameraEntity); + if (CameraRenderingData == nullptr) + { + LOG.Add("Camera rendering data is nullptr in FERenderer::SaveScreenshot", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } -void FERenderer::TakeScreenshot(const char* FileName, const int Width, const int Height) -{ - unsigned char* pixels = new unsigned char[4 * Width * Height]; + FETexture* CameraResult = RENDERER.GetCameraResult(MainCameraEntity); + + unsigned char* Pixels = new unsigned char[4 * CameraResult->GetWidth() * CameraResult->GetHeight()]; FE_GL_ERROR(glActiveTexture(GL_TEXTURE0)); - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, PostProcessEffects.back()->Stages.back()->OutTexture->GetTextureID())); - FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); - - FETexture* TempTexture = RESOURCE_MANAGER.RawDataToFETexture(pixels, Width, Height); - RESOURCE_MANAGER.SaveFETexture(TempTexture, FileName); + FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, CameraResult->GetTextureID())); + FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, Pixels)); + + FETexture* TempTexture = RESOURCE_MANAGER.RawDataToFETexture(Pixels, CameraResult->GetWidth(), CameraResult->GetHeight()); + RESOURCE_MANAGER.SaveFETexture(TempTexture, FileName.c_str()); RESOURCE_MANAGER.DeleteFETexture(TempTexture); - //RESOURCE_MANAGER.saveFETexture(fileName, pixels, width, height); - delete[] pixels; + delete[] Pixels; } -void FERenderer::RenderEntity(const FEEntity* Entity, const FEBasicCamera* CurrentCamera, const bool bReloadUniformBlocks, const int ComponentIndex) +void FERenderer::RenderGameModelComponent(FEEntity* Entity, FEEntity* Camera, bool bReloadUniformBlocks) { - if (Entity->IsWireframeMode()) - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - - if (bReloadUniformBlocks) - LoadUniformBlocks(); - - if (ComponentIndex == -1) - { - for (size_t i = 0; i < Entity->Prefab->Components.size(); i++) - { - if (Entity->Prefab->Components[i]->GameModel == nullptr) - { - LOG.Add("Trying to draw Entity with GameModel that is nullptr in FERenderer::RenderEntity", "FE_LOG_RENDERING", FE_LOG_ERROR); - continue; - } - - if (Entity->Prefab->Components[i]->GameModel->Material == nullptr) - { - LOG.Add("Trying to draw Entity with Material that is nullptr in FERenderer::RenderEntity", "FE_LOG_RENDERING", FE_LOG_ERROR); - continue; - } - - if (Entity->Prefab->Components[i]->GameModel->Material->Shader == nullptr) - { - LOG.Add("Trying to draw Entity with Shader that is nullptr in FERenderer::RenderEntity", "FE_LOG_RENDERING", FE_LOG_ERROR); - continue; - } - - FEShader* OriginalShader = Entity->Prefab->Components[i]->GameModel->Material->Shader; - if (OriginalShader == nullptr) - continue; + if (Entity == nullptr || !Entity->HasComponent()) + return; - if (ShaderToForce) - { - if (OriginalShader->GetName() == "FEPBRShader") - Entity->Prefab->Components[i]->GameModel->Material->Shader = ShaderToForce; - } + FETransformComponent& TransformComponent = Entity->GetComponent(); + FEGameModelComponent& GameModelComponent = Entity->GetComponent(); - Entity->Prefab->Components[i]->GameModel->Material->Bind(); - FETransformComponent TempTransform; - if (Entity->Prefab->Components.size() == 1) - { - TempTransform = Entity->Transform; - } - else - { - TempTransform = Entity->Transform.Combine(Entity->Prefab->Components[i]->Transform); - } - LoadStandardParams(Entity->Prefab->Components[i]->GameModel->Material->Shader, CurrentCamera, Entity->Prefab->Components[i]->GameModel->Material, &TempTransform, Entity->IsReceivingShadows(), Entity->IsUniformLighting()); - Entity->Prefab->Components[i]->GameModel->Material->Shader->LoadDataToGPU(); - - FE_GL_ERROR(glBindVertexArray(Entity->Prefab->Components[i]->GameModel->Mesh->GetVaoID())); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); - - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_INDEX) == FE_INDEX) - FE_GL_ERROR(glDrawElements(GL_TRIANGLES, Entity->Prefab->Components[i]->GameModel->Mesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_INDEX) != FE_INDEX) - FE_GL_ERROR(glDrawArrays(GL_TRIANGLES, 0, Entity->Prefab->Components[i]->GameModel->Mesh->GetVertexCount())); - - FE_GL_ERROR(glBindVertexArray(0)); + RenderGameModelComponent(GameModelComponent, TransformComponent, Entity->ParentScene, Camera, bReloadUniformBlocks); +} - Entity->Prefab->Components[i]->GameModel->Material->UnBind(); +void FERenderer::RenderGameModelComponent(FEGameModelComponent& GameModelComponent, FETransformComponent& TransformComponent, FEScene* ParentScene, FEEntity* Camera, bool bReloadUniformBlocks) +{ + if (ParentScene == nullptr || GameModelComponent.GetGameModel() == nullptr || GameModelComponent.GetGameModel()->Mesh == nullptr || GameModelComponent.GetGameModel()->Material == nullptr) + return; - if (ShaderToForce) - { - if (OriginalShader->GetName() == "FEPBRShader") - Entity->Prefab->Components[i]->GameModel->Material->Shader = OriginalShader; - } - } + if (GameModelComponent.IsWireframeMode()) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + if (bReloadUniformBlocks) + LoadUniformBlocks(ParentScene); + + FEGameModel* GameModel = GameModelComponent.GetGameModel(); + if (GameModel == nullptr) + { + LOG.Add("Trying to draw FEGameModelComponent with GameModel that is nullptr in FERenderer::RenderGameModelComponent", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; } - else + + FEShader* OriginalShader = GameModel->Material->Shader; + if (ShaderToForce) { - FEShader* OriginalShader = Entity->Prefab->Components[ComponentIndex]->GameModel->Material->Shader; - if (ShaderToForce) - { - if (OriginalShader->GetName() == "FEPBRShader") - Entity->Prefab->Components[ComponentIndex]->GameModel->Material->Shader = ShaderToForce; - } + if (OriginalShader->GetName() == "FEPBRShader") + GameModel->Material->Shader = ShaderToForce; + } - Entity->Prefab->Components[ComponentIndex]->GameModel->Material->Bind(); - const FETransformComponent TempTransform = Entity->Transform.Combine(Entity->Prefab->Components[ComponentIndex]->Transform); - LoadStandardParams(Entity->Prefab->Components[ComponentIndex]->GameModel->Material->Shader, CurrentCamera, Entity->Prefab->Components[ComponentIndex]->GameModel->Material, &TempTransform, Entity->IsReceivingShadows(), Entity->IsUniformLighting()); - Entity->Prefab->Components[ComponentIndex]->GameModel->Material->Shader->LoadDataToGPU(); + GameModel->Material->Bind(); + LoadStandardUniforms(GameModel->Material->Shader, GameModel->Material, &TransformComponent, Camera, GameModelComponent.IsReceivingShadows(), GameModelComponent.IsUniformLighting()); + GameModel->Material->Shader->LoadUniformsDataToGPU(); - FE_GL_ERROR(glBindVertexArray(Entity->Prefab->Components[ComponentIndex]->GameModel->Mesh->GetVaoID())); - if ((Entity->Prefab->Components[ComponentIndex]->GameModel->Mesh->VertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); - if ((Entity->Prefab->Components[ComponentIndex]->GameModel->Mesh->VertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); - if ((Entity->Prefab->Components[ComponentIndex]->GameModel->Mesh->VertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); - if ((Entity->Prefab->Components[ComponentIndex]->GameModel->Mesh->VertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); - if ((Entity->Prefab->Components[ComponentIndex]->GameModel->Mesh->VertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); - if ((Entity->Prefab->Components[ComponentIndex]->GameModel->Mesh->VertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); + FE_GL_ERROR(glBindVertexArray(GameModel->Mesh->GetVaoID())); + if ((GameModel->Mesh->VertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); + if ((GameModel->Mesh->VertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); + if ((GameModel->Mesh->VertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); + if ((GameModel->Mesh->VertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); + if ((GameModel->Mesh->VertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); + if ((GameModel->Mesh->VertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); - if ((Entity->Prefab->Components[ComponentIndex]->GameModel->Mesh->VertexAttributes & FE_INDEX) == FE_INDEX) - FE_GL_ERROR(glDrawElements(GL_TRIANGLES, Entity->Prefab->Components[ComponentIndex]->GameModel->Mesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); - if ((Entity->Prefab->Components[ComponentIndex]->GameModel->Mesh->VertexAttributes & FE_INDEX) != FE_INDEX) - FE_GL_ERROR(glDrawArrays(GL_TRIANGLES, 0, Entity->Prefab->Components[ComponentIndex]->GameModel->Mesh->GetVertexCount())); + if ((GameModel->Mesh->VertexAttributes & FE_INDEX) == FE_INDEX) + FE_GL_ERROR(glDrawElements(GL_TRIANGLES, GameModel->Mesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); + if ((GameModel->Mesh->VertexAttributes & FE_INDEX) != FE_INDEX) + FE_GL_ERROR(glDrawArrays(GL_TRIANGLES, 0, GameModel->Mesh->GetVertexCount())); - FE_GL_ERROR(glBindVertexArray(0)); + FE_GL_ERROR(glBindVertexArray(0)); - Entity->Prefab->Components[ComponentIndex]->GameModel->Material->UnBind(); + GameModel->Material->UnBind(); - if (ShaderToForce) - { - if (OriginalShader->GetName() == "FEPBRShader") - Entity->Prefab->Components[ComponentIndex]->GameModel->Material->Shader = OriginalShader; - } + if (ShaderToForce) + { + if (OriginalShader->GetName() == "FEPBRShader") + GameModel->Material->Shader = OriginalShader; } - if (Entity->IsWireframeMode()) + if (GameModelComponent.IsWireframeMode()) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } -void FERenderer::RenderEntityForward(const FEEntity* Entity, const FEBasicCamera* CurrentCamera, const bool bReloadUniformBlocks) +// FIXME: Should this be implemented as a separate function, or should it only be called when the camera is in forward rendering mode? +// Note: This function is currently only used in VR rendering and Simplified mode! +void FERenderer::RenderGameModelComponentForward(FEEntity* Entity, FEEntity* Camera, bool bReloadUniformBlocks) { + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FETransformComponent& TransformComponent = Entity->GetComponent(); + FEGameModelComponent& GameModelComponent = Entity->GetComponent(); + if (bReloadUniformBlocks) - LoadUniformBlocks(); + LoadUniformBlocks(Entity->ParentScene); + + FEGameModel* GameModel = GameModelComponent.GetGameModel(); + if (GameModel == nullptr) + { + LOG.Add("Trying to draw FEGameModelComponent with GameModel that is nullptr in FERenderer::RenderGameModelComponent", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } + + if (GameModelComponent.IsWireframeMode()) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - for (size_t i = 0; i < Entity->Prefab->Components.size(); i++) + FECameraComponent& CurrentCameraComponent = Camera->GetComponent(); + + FEShader* OriginalShader = nullptr; + if (!(CurrentCameraComponent.GetRenderingPipeline() == FERenderingPipeline::Forward_Simplified) || RENDERER.bVRActive) { - FEShader* OriginalShader = nullptr; - if (!bSimplifiedRendering || RENDERER.bVRActive) + OriginalShader = GameModel->Material->Shader; + // FIXME: This should not depend on RENDERER.bVRActive! + if (RENDERER.bVRActive) { - OriginalShader = Entity->Prefab->Components[i]->GameModel->Material->Shader; - if (RENDERER.bVRActive) - { - if (OriginalShader->GetObjectID() != "6917497A5E0C05454876186F"/*"SolidColorMaterial"*/) - Entity->Prefab->Components[i]->GameModel->Material->Shader = RESOURCE_MANAGER.GetShader("5E45017E664A62273E191500"/*"FEPBRShaderForward"*/); - } - else - { - Entity->Prefab->Components[i]->GameModel->Material->Shader = RESOURCE_MANAGER.GetShader("5E45017E664A62273E191500"/*"FEPBRShaderForward"*/); - } + if (OriginalShader->GetObjectID() != "6917497A5E0C05454876186F"/*"SolidColorMaterial"*/) + GameModel->Material->Shader = RESOURCE_MANAGER.GetShader("5E45017E664A62273E191500"/*"FEPBRShaderForward"*/); + } + else + { + GameModel->Material->Shader = RESOURCE_MANAGER.GetShader("5E45017E664A62273E191500"/*"FEPBRShaderForward"*/); } + } - Entity->Prefab->Components[i]->GameModel->Material->Bind(); - LoadStandardParams(Entity->Prefab->Components[i]->GameModel->Material->Shader, CurrentCamera, Entity->Prefab->Components[i]->GameModel->Material, &Entity->Transform, Entity->IsReceivingShadows(), Entity->IsUniformLighting()); - Entity->Prefab->Components[i]->GameModel->Material->Shader->LoadDataToGPU(); + GameModel->Material->Bind(); + LoadStandardUniforms(GameModel->Material->Shader, GameModel->Material, &TransformComponent, Camera, GameModelComponent.IsReceivingShadows(), GameModelComponent.IsUniformLighting()); + GameModel->Material->Shader->LoadUniformsDataToGPU(); - FE_GL_ERROR(glBindVertexArray(Entity->Prefab->Components[i]->GameModel->Mesh->GetVaoID())); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); + FE_GL_ERROR(glBindVertexArray(GameModel->Mesh->GetVaoID())); + if ((GameModel->Mesh->VertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); + if ((GameModel->Mesh->VertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); + if ((GameModel->Mesh->VertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); + if ((GameModel->Mesh->VertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); + if ((GameModel->Mesh->VertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); + if ((GameModel->Mesh->VertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_INDEX) == FE_INDEX) - FE_GL_ERROR(glDrawElements(GL_TRIANGLES, Entity->Prefab->Components[i]->GameModel->Mesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); - if ((Entity->Prefab->Components[i]->GameModel->Mesh->VertexAttributes & FE_INDEX) != FE_INDEX) - FE_GL_ERROR(glDrawArrays(GL_TRIANGLES, 0, Entity->Prefab->Components[i]->GameModel->Mesh->GetVertexCount())); + if ((GameModel->Mesh->VertexAttributes & FE_INDEX) == FE_INDEX) + FE_GL_ERROR(glDrawElements(GL_TRIANGLES, GameModel->Mesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); + if ((GameModel->Mesh->VertexAttributes & FE_INDEX) != FE_INDEX) + FE_GL_ERROR(glDrawArrays(GL_TRIANGLES, 0, GameModel->Mesh->GetVertexCount())); - FE_GL_ERROR(glBindVertexArray(0)); + FE_GL_ERROR(glBindVertexArray(0)); - Entity->Prefab->Components[i]->GameModel->Material->UnBind(); + GameModel->Material->UnBind(); - if (!bSimplifiedRendering || RENDERER.bVRActive) - Entity->Prefab->Components[i]->GameModel->Material->Shader = OriginalShader; - } + if (!(CurrentCameraComponent.GetRenderingPipeline() == FERenderingPipeline::Forward_Simplified) || RENDERER.bVRActive) + GameModel->Material->Shader = OriginalShader; + + if (GameModelComponent.IsWireframeMode()) + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } -void FERenderer::RenderTerrain(FETerrain* Terrain, const FEBasicCamera* CurrentCamera) +void FERenderer::RenderTerrainComponent(FEEntity* TerrainEntity, FEEntity* Camera) { - if (Terrain->IsWireframeMode()) + if (TerrainEntity == nullptr) + { + LOG.Add("FERenderer::RenderTerrainComponent TerrainEntity is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + if (!TerrainEntity->HasComponent()) + { + LOG.Add("FERenderer::RenderTerrainComponent TerrainEntity does not have valid FETerrainComponent", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + FETransformComponent& TransformComponent = TerrainEntity->GetComponent(); + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (TerrainComponent.Shader == nullptr) + { + LOG.Add("FERenderer::RenderTerrainComponent TerrainComponent does not have valid Shader", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + if (TerrainComponent.HeightMap == nullptr) + { + LOG.Add("FERenderer::RenderTerrainComponent TerrainComponent does not have valid HeightMap", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + if (TerrainComponent.IsWireframeMode()) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - if (Terrain->Shader->GetName() == "FESMTerrainShader") + if (TerrainComponent.Shader->GetName() == "FESMTerrainShader") { - Terrain->HeightMap->Bind(0); + TerrainComponent.HeightMap->Bind(0); } else { - for (size_t i = 0; i < Terrain->Layers.size(); i++) + for (size_t i = 0; i < TerrainComponent.Layers.size(); i++) { - if (Terrain->Layers[i] != nullptr && Terrain->Layers[i]->GetMaterial()->IsCompackPacking()) + if (TerrainComponent.Layers[i] != nullptr && TerrainComponent.Layers[i]->GetMaterial()->IsCompackPacking()) { - if (Terrain->Layers[i]->GetMaterial()->GetAlbedoMap() != nullptr) - Terrain->Layers[i]->GetMaterial()->GetAlbedoMap()->Bind(static_cast(i * 3)); + if (TerrainComponent.Layers[i]->GetMaterial()->GetAlbedoMap() != nullptr) + TerrainComponent.Layers[i]->GetMaterial()->GetAlbedoMap()->Bind(static_cast(i * 3)); - if (Terrain->Layers[i]->GetMaterial()->GetNormalMap() != nullptr) - Terrain->Layers[i]->GetMaterial()->GetNormalMap()->Bind(static_cast(i * 3 + 1)); + if (TerrainComponent.Layers[i]->GetMaterial()->GetNormalMap() != nullptr) + TerrainComponent.Layers[i]->GetMaterial()->GetNormalMap()->Bind(static_cast(i * 3 + 1)); - if (Terrain->Layers[i]->GetMaterial()->GetAOMap() != nullptr) - Terrain->Layers[i]->GetMaterial()->GetAOMap()->Bind(static_cast(i * 3 + 2)); + if (TerrainComponent.Layers[i]->GetMaterial()->GetAOMap() != nullptr) + TerrainComponent.Layers[i]->GetMaterial()->GetAOMap()->Bind(static_cast(i * 3 + 2)); } } - Terrain->HeightMap->Bind(24); - if (Terrain->ProjectedMap != nullptr) - Terrain->ProjectedMap->Bind(25); + TerrainComponent.HeightMap->Bind(24); + if (TerrainComponent.ProjectedMap != nullptr) + TerrainComponent.ProjectedMap->Bind(25); for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS / FE_TERRAIN_LAYER_PER_TEXTURE; i++) { - if (Terrain->LayerMaps[i] != nullptr) - Terrain->LayerMaps[i]->Bind(static_cast(26 + i)); + if (TerrainComponent.LayerMaps[i] != nullptr) + TerrainComponent.LayerMaps[i]->Bind(static_cast(26 + i)); } } - Terrain->Shader->Start(); - LoadStandardParams(Terrain->Shader, CurrentCamera, nullptr, &Terrain->Transform, Terrain->IsReceivingShadows()); - // ************ Load materials data for all terrain layers ************ + TerrainComponent.Shader->Start(); + LoadStandardUniforms(TerrainComponent.Shader, nullptr, &TransformComponent, Camera, TerrainComponent.IsReceivingShadows()); + // ************ Load materials data for all Terrain layers ************ - const int LayersUsed = Terrain->LayersUsed(); + const int LayersUsed = TerrainComponent.LayersUsed(); if (LayersUsed == 0) { // 0 index is for hightMap. RESOURCE_MANAGER.NoTexture->Bind(1); } - Terrain->LoadLayersDataToGPU(); - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, Terrain->GPULayersDataBuffer)); + TerrainComponent.LoadLayersDataToGPU(); + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, TerrainComponent.GPULayersDataBuffer)); // Shadow map shader does not have this parameter. - if (Terrain->Shader->GetParameter("usedLayersCount") != nullptr) - Terrain->Shader->UpdateParameterData("usedLayersCount", static_cast(LayersUsed)); - // ************ Load materials data for all terrain layers END ************ + if (TerrainComponent.Shader->GetUniform("usedLayersCount") != nullptr) + TerrainComponent.Shader->UpdateUniformData("usedLayersCount", static_cast(LayersUsed)); + // ************ Load materials data for all Terrain layers END ************ - Terrain->Shader->UpdateParameterData("hightScale", Terrain->HightScale); - Terrain->Shader->UpdateParameterData("scaleFactor", Terrain->ScaleFactor); - if (Terrain->Shader->GetName() != "FESMTerrainShader") - Terrain->Shader->UpdateParameterData("tileMult", Terrain->TileMult); - Terrain->Shader->UpdateParameterData("LODlevel", Terrain->LODLevel); - Terrain->Shader->UpdateParameterData("hightMapShift", Terrain->HightMapShift); + TerrainComponent.Shader->UpdateUniformData("hightScale", TerrainComponent.HightScale); + TerrainComponent.Shader->UpdateUniformData("scaleFactor", TerrainComponent.ScaleFactor); + if (TerrainComponent.Shader->GetName() != "FESMTerrainShader") + TerrainComponent.Shader->UpdateUniformData("tileMult", TerrainComponent.TileMult); + TerrainComponent.Shader->UpdateUniformData("LODlevel", TerrainComponent.LODLevel); + TerrainComponent.Shader->UpdateUniformData("hightMapShift", TerrainComponent.HightMapShift); - static glm::vec3 PivotPosition = Terrain->Transform.GetPosition(); - PivotPosition = Terrain->Transform.GetPosition(); - Terrain->ScaleFactor = 1.0f * Terrain->ChunkPerSide; + glm::vec3 PivotPosition = TransformComponent.GetPosition(); + TerrainComponent.ScaleFactor = 1.0f * TerrainComponent.ChunkPerSide; static int PVMHash = static_cast(std::hash{}("FEPVMMatrix")); static int WorldMatrixHash = static_cast(std::hash{}("FEWorldMatrix")); static int HightMapShiftHash = static_cast(std::hash{}("hightMapShift")); - const bool bWasDirty = Terrain->Transform.bDirtyFlag; - Terrain->Shader->LoadDataToGPU(); - for (size_t i = 0; i < Terrain->ChunkPerSide; i++) + TerrainComponent.Shader->LoadUniformsDataToGPU(); + FETransformComponent OldState = TransformComponent; + for (size_t i = 0; i < TerrainComponent.ChunkPerSide; i++) { - for (size_t j = 0; j < Terrain->ChunkPerSide; j++) + for (size_t j = 0; j < TerrainComponent.ChunkPerSide; j++) { - Terrain->Transform.SetPosition(glm::vec3(PivotPosition.x + i * 64.0f * Terrain->Transform.Scale[0], PivotPosition.y, PivotPosition.z + j * 64.0f * Terrain->Transform.Scale[2])); + // Kind of hacky code + // Revert to old state to avoid any changes in TransformComponent. + TransformComponent = OldState; + glm::mat4 ParentMatrix = TransformComponent.GetParentMatrix(); + // Use here SetWorldPosition instead to avoid usage of LocalSpaceMatrix directly. + TransformComponent.SetPosition(glm::vec3(PivotPosition.x + i * 64.0f * TransformComponent.Scale[0], PivotPosition.y, PivotPosition.z + j * 64.0f * TransformComponent.Scale[2])); + // Not to wait for scene hierarchy update. + TransformComponent.WorldSpaceMatrix = ParentMatrix * TransformComponent.GetLocalMatrix(); - Terrain->Shader->UpdateParameterData("FEPVMMatrix", CurrentCamera->GetProjectionMatrix() * CurrentCamera->GetViewMatrix() * Terrain->Transform.GetTransformMatrix()); - if (Terrain->Shader->GetParameter("FEWorldMatrix") != nullptr) - Terrain->Shader->UpdateParameterData("FEWorldMatrix", Terrain->Transform.GetTransformMatrix()); - Terrain->Shader->UpdateParameterData("hightMapShift", glm::vec2(i * -1.0f, j * -1.0f)); + FECameraComponent& CurrentCameraComponent = Camera->GetComponent(); + FETransformComponent& CurrentCameraTransformComponent = Camera->GetComponent(); - Terrain->Shader->LoadMatrix(PVMHash, *static_cast(Terrain->Shader->GetParameter("FEPVMMatrix")->Data)); - if (Terrain->Shader->GetParameter("FEWorldMatrix") != nullptr) - Terrain->Shader->LoadMatrix(WorldMatrixHash, *static_cast(Terrain->Shader->GetParameter("FEWorldMatrix")->Data)); + TerrainComponent.Shader->UpdateUniformData("FEPVMMatrix", CurrentCameraComponent.GetProjectionMatrix() * CurrentCameraComponent.GetViewMatrix() * TransformComponent.GetWorldMatrix()); + if (TerrainComponent.Shader->GetUniform("FEWorldMatrix") != nullptr) + TerrainComponent.Shader->UpdateUniformData("FEWorldMatrix", TransformComponent.GetWorldMatrix()); + TerrainComponent.Shader->UpdateUniformData("hightMapShift", glm::vec2(i * -1.0f, j * -1.0f)); - if (Terrain->Shader->GetParameter("hightMapShift") != nullptr) - Terrain->Shader->LoadVector(HightMapShiftHash, *static_cast(Terrain->Shader->GetParameter("hightMapShift")->Data)); + TerrainComponent.Shader->LoadUniformDataToGPU("FEPVMMatrix"); - Terrain->Render(); + if (TerrainComponent.Shader->GetUniform("FEWorldMatrix") != nullptr) + TerrainComponent.Shader->LoadUniformDataToGPU("FEWorldMatrix"); + + TerrainComponent.Shader->LoadUniformDataToGPU("hightMapShift"); + + FE_GL_ERROR(glDrawArraysInstanced(GL_PATCHES, 0, 4, 64 * 64)); } } - Terrain->Shader->Stop(); - Terrain->Transform.SetPosition(PivotPosition); + TerrainComponent.Shader->Stop(); + // Revert to old state to avoid any changes in TransformComponent. + TransformComponent = OldState; - if (!bWasDirty) - Terrain->Transform.bDirtyFlag = false; - - if (Terrain->IsWireframeMode()) + if (TerrainComponent.IsWireframeMode()) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } @@ -1487,245 +1716,79 @@ void FERenderer::DrawLine(const glm::vec3 BeginPoint, const glm::vec3 EndPoint, LineCounter++; } -void FERenderer::UpdateTerrainBrush(FETerrain* Terrain) +void FERenderer::UpdateShadersForCamera(FECameraRenderingData* CameraData) { - Terrain->UpdateBrush(EngineMainCamera->Position, MouseRay); - FE_GL_ERROR(glViewport(0, 0, SceneToTextureFB->GetWidth(), SceneToTextureFB->GetHeight())); -} + if (CameraData == nullptr) + return; -bool FERenderer::IsSkyEnabled() -{ - return SkyDome->IsVisible(); -} + FECameraComponent& CameraComponent = CameraData->CameraEntity->GetComponent(); + if (CameraComponent.GetRenderingPipeline() != FERenderingPipeline::Deferred) + return; -void FERenderer::SetSkyEnabled(const bool NewValue) -{ - SkyDome->SetVisibility(NewValue); -} + glm::ivec2 ViewportSize = glm::ivec2(CameraComponent.GetRenderTargetWidth(), CameraComponent.GetRenderTargetHeight()); + if (CameraComponent.GetRenderScale() != 1.0f) + { + ViewportSize.x = CameraComponent.Viewport->GetWidth(); + ViewportSize.y = CameraComponent.Viewport->GetHeight(); + } + + // **************************** Bloom ******************************** + CameraData->PostProcessEffects[0]->Stages[0]->Shader->UpdateUniformData("thresholdBrightness", CameraComponent.GetBloomThreshold()); -float FERenderer::GetDistanceToSky() -{ - return SkyDome->Transform.Scale[0]; -} + CameraData->PostProcessEffects[0]->Stages[1]->StageSpecificUniformValues[1].SetValue(CameraComponent.GetBloomSize()); + CameraData->PostProcessEffects[0]->Stages[2]->StageSpecificUniformValues[1].SetValue(CameraComponent.GetBloomSize()); + // **************************** Bloom END **************************** -void FERenderer::SetDistanceToSky(const float NewValue) -{ - SkyDome->Transform.SetScale(glm::vec3(NewValue)); -} + // **************************** FXAA ******************************** + CameraData->PostProcessEffects[2]->Stages[0]->Shader->UpdateUniformData("FXAASpanMax", CameraComponent.GetFXAASpanMax()); + CameraData->PostProcessEffects[2]->Stages[0]->Shader->UpdateUniformData("FXAAReduceMin", CameraComponent.GetFXAAReduceMin()); + CameraData->PostProcessEffects[2]->Stages[0]->Shader->UpdateUniformData("FXAAReduceMul", CameraComponent.GetFXAAReduceMul()); + CameraData->PostProcessEffects[2]->Stages[0]->Shader->UpdateUniformData("FXAATextureSize", glm::vec2(1.0f / ViewportSize.x, 1.0f / ViewportSize.y)); + // **************************** FXAA END **************************** -bool FERenderer::IsDistanceFogEnabled() -{ - return bDistanceFogEnabled; -} + // **************************** Depth of Field ******************************** + CameraData->PostProcessEffects[3]->Stages[0]->Shader->UpdateUniformData("depthThreshold", CameraComponent.GetDOFNearDistance()); + CameraData->PostProcessEffects[3]->Stages[1]->Shader->UpdateUniformData("depthThreshold", CameraComponent.GetDOFNearDistance()); -void FERenderer::SetDistanceFogEnabled(const bool NewValue) -{ - if (bDistanceFogEnabled == false && NewValue == true) - { - if (DistanceFogDensity <= 0.0f) - DistanceFogDensity = 0.007f; - if (DistanceFogGradient <= 0.0f) - DistanceFogGradient = 2.5f; - } - bDistanceFogEnabled = NewValue; - UpdateFogInShaders(); -} + CameraData->PostProcessEffects[3]->Stages[0]->Shader->UpdateUniformData("depthThresholdFar", CameraComponent.GetDOFFarDistance()); + CameraData->PostProcessEffects[3]->Stages[1]->Shader->UpdateUniformData("depthThresholdFar", CameraComponent.GetDOFFarDistance()); -float FERenderer::GetDistanceFogDensity() -{ - return DistanceFogDensity; -} + CameraData->PostProcessEffects[3]->Stages[0]->Shader->UpdateUniformData("blurSize", CameraComponent.GetDOFStrength()); + CameraData->PostProcessEffects[3]->Stages[1]->Shader->UpdateUniformData("blurSize", CameraComponent.GetDOFStrength()); -void FERenderer::SetDistanceFogDensity(const float NewValue) -{ - DistanceFogDensity = NewValue; - UpdateFogInShaders(); -} + CameraData->PostProcessEffects[3]->Stages[0]->Shader->UpdateUniformData("intMult", CameraComponent.GetDOFDistanceDependentStrength()); + CameraData->PostProcessEffects[3]->Stages[1]->Shader->UpdateUniformData("intMult", CameraComponent.GetDOFDistanceDependentStrength()); -float FERenderer::GetDistanceFogGradient() -{ - return DistanceFogGradient; -} + CameraData->PostProcessEffects[3]->Stages[0]->Shader->UpdateUniformData("zNear", CameraComponent.GetNearPlane()); + CameraData->PostProcessEffects[3]->Stages[1]->Shader->UpdateUniformData("zNear", CameraComponent.GetNearPlane()); -void FERenderer::SetDistanceFogGradient(const float NewValue) -{ - DistanceFogGradient = NewValue; - UpdateFogInShaders(); -} + CameraData->PostProcessEffects[3]->Stages[0]->Shader->UpdateUniformData("zFar", CameraComponent.GetFarPlane()); + CameraData->PostProcessEffects[3]->Stages[1]->Shader->UpdateUniformData("zFar", CameraComponent.GetFarPlane()); + // **************************** Depth of Field END **************************** -void FERenderer::UpdateFogInShaders() -{ - if (bDistanceFogEnabled) + // **************************** Chromatic Aberration ******************************** + CameraData->PostProcessEffects[4]->Stages[0]->Shader->UpdateUniformData("intensity", CameraComponent.GetChromaticAberrationIntensity()); + // **************************** Chromatic Aberration END **************************** + + // **************************** Distance Fog ******************************** + + if (CameraComponent.IsDistanceFogEnabled()) { - RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateParameterData("fogDensity", DistanceFogDensity); - RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateParameterData("fogGradient", DistanceFogGradient); + RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateUniformData("fogDensity", CameraComponent.GetDistanceFogDensity()); + RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateUniformData("fogGradient", CameraComponent.GetDistanceFogGradient()); - RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateParameterData("fogDensity", DistanceFogDensity); - RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateParameterData("fogGradient", DistanceFogGradient); + RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateUniformData("fogDensity", CameraComponent.GetDistanceFogDensity()); + RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateUniformData("fogGradient", CameraComponent.GetDistanceFogGradient()); } else { - RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateParameterData("fogDensity", -1.0f); - RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateParameterData("fogGradient", -1.0f); + RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateUniformData("fogDensity", -1.0f); + RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateUniformData("fogGradient", -1.0f); - RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateParameterData("fogDensity", -1.0f); - RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateParameterData("fogGradient", -1.0f); + RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateUniformData("fogDensity", -1.0f); + RESOURCE_MANAGER.GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateUniformData("fogGradient", -1.0f); } -} - -float FERenderer::GetChromaticAberrationIntensity() -{ - if (GetPostProcessEffect("506D804162647749060C3E68"/*"chromaticAberration"*/) == nullptr) - return 0.0f; - return *static_cast(GetPostProcessEffect("506D804162647749060C3E68"/*"chromaticAberration"*/)->Stages[0]->Shader-> - GetParameter("intensity")->Data); -} - -void FERenderer::SetChromaticAberrationIntensity(const float NewValue) -{ - if (GetPostProcessEffect("506D804162647749060C3E68"/*"chromaticAberration"*/) == nullptr) - return; - GetPostProcessEffect("506D804162647749060C3E68"/*"chromaticAberration"*/)->Stages[0]->Shader->UpdateParameterData("intensity", NewValue); -} - -float FERenderer::GetDOFNearDistance() -{ - if (GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/) == nullptr) - return 0.0f; - return *static_cast(GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[0]->Shader-> - GetParameter("depthThreshold")->Data); -} - -void FERenderer::SetDOFNearDistance(const float NewValue) -{ - if (GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/) == nullptr) - return; - GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[0]->Shader->UpdateParameterData("depthThreshold", NewValue); - GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[1]->Shader->UpdateParameterData("depthThreshold", NewValue); -} - -float FERenderer::GetDOFFarDistance() -{ - if (GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/) == nullptr) - return 0.0f; - return *static_cast(GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[0]->Shader-> - GetParameter("depthThresholdFar")->Data); -} - -void FERenderer::SetDOFFarDistance(const float NewValue) -{ - if (GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/) == nullptr) - return; - GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[0]->Shader->UpdateParameterData("depthThresholdFar", NewValue); - GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[1]->Shader->UpdateParameterData("depthThresholdFar", NewValue); -} - -float FERenderer::GetDOFStrength() -{ - if (GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/) == nullptr) - return 0.0f; - return *static_cast(GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[0]->Shader-> - GetParameter("blurSize")->Data); -} - -void FERenderer::SetDOFStrength(const float NewValue) -{ - if (GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/) == nullptr) - return; - GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[0]->Shader->UpdateParameterData("blurSize", NewValue); - GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[1]->Shader->UpdateParameterData("blurSize", NewValue); -} - -float FERenderer::GetDOFDistanceDependentStrength() -{ - if (GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/) == nullptr) - return 0.0f; - return *static_cast(GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[0]->Shader-> - GetParameter("intMult")->Data); -} - -void FERenderer::SetDOFDistanceDependentStrength(const float NewValue) -{ - if (GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/) == nullptr) - return; - GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[0]->Shader->UpdateParameterData("intMult", NewValue); - GetPostProcessEffect("217C4E80482B6C650D7B492F"/*"DOF"*/)->Stages[1]->Shader->UpdateParameterData("intMult", NewValue); -} - -float FERenderer::GetBloomThreshold() -{ - if (GetPostProcessEffect("451C48791871283D372C5938"/*"bloom"*/) == nullptr) - return 0.0f; - return *static_cast(GetPostProcessEffect("451C48791871283D372C5938"/*"bloom"*/)->Stages[0]->Shader->GetParameter( - "thresholdBrightness")->Data); -} - -void FERenderer::SetBloomThreshold(const float NewValue) -{ - if (GetPostProcessEffect("451C48791871283D372C5938"/*"bloom"*/) == nullptr) - return; - GetPostProcessEffect("451C48791871283D372C5938"/*"bloom"*/)->Stages[0]->Shader->UpdateParameterData("thresholdBrightness", NewValue); -} - -float FERenderer::GetBloomSize() -{ - if (GetPostProcessEffect("451C48791871283D372C5938"/*"bloom"*/) == nullptr) - return 0.0f; - return *static_cast(GetPostProcessEffect("451C48791871283D372C5938"/*"bloom"*/)->Stages[1]->StageSpecificUniforms[1]. - Data); -} - -void FERenderer::SetBloomSize(const float NewValue) -{ - if (GetPostProcessEffect("451C48791871283D372C5938"/*"bloom"*/) == nullptr) - return; - *static_cast(GetPostProcessEffect("451C48791871283D372C5938"/*"bloom"*/)->Stages[1]->StageSpecificUniforms[1].Data) = NewValue; - *static_cast(GetPostProcessEffect("451C48791871283D372C5938"/*"bloom"*/)->Stages[2]->StageSpecificUniforms[1].Data) = NewValue; -} - -float FERenderer::GetFXAASpanMax() -{ - if (GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/) == nullptr) - return 0.0f; - return *static_cast(GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/)->Stages[0]->Shader-> - GetParameter("FXAASpanMax")->Data); -} - -void FERenderer::SetFXAASpanMax(const float NewValue) -{ - if (GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/) == nullptr) - return; - GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/)->Stages[0]->Shader->UpdateParameterData("FXAASpanMax", NewValue); -} - -float FERenderer::GetFXAAReduceMin() -{ - if (GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/) == nullptr) - return 0.0f; - return *static_cast(GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/)->Stages[0]->Shader-> - GetParameter("FXAAReduceMin")->Data); -} - -void FERenderer::SetFXAAReduceMin(const float NewValue) -{ - if (GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/) == nullptr) - return; - GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/)->Stages[0]->Shader->UpdateParameterData("FXAAReduceMin", NewValue); -} - -float FERenderer::GetFXAAReduceMul() -{ - if (GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/) == nullptr) - return 0.0f; - return *static_cast(GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/)->Stages[0]->Shader-> - GetParameter("FXAAReduceMul")->Data); -} - -void FERenderer::SetFXAAReduceMul(const float NewValue) -{ - if (GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/) == nullptr) - return; - GetPostProcessEffect("0A3F10643F06525D70016070"/*"FE_FXAA"*/)->Stages[0]->Shader->UpdateParameterData("FXAAReduceMul", NewValue); + // **************************** Distance Fog END **************************** } void FERenderer::DrawAABB(FEAABB AABB, const glm::vec3 Color, const float LineWidth) @@ -1754,79 +1817,78 @@ void FERenderer::ForceShader(FEShader* Shader) ShaderToForce = Shader; } -void FERenderer::UpdateGPUCullingFrustum(float** Frustum, glm::vec3 CameraPosition) +void FERenderer::UpdateGPUCullingFrustum(FEEntity* Camera) { float* FrustumBufferData = static_cast(glMapNamedBufferRange(FrustumInfoBuffer, 0, sizeof(float) * (32), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_UNSYNCHRONIZED_BIT)); + FECameraComponent& CurrentCameraComponent = Camera->GetComponent(); + FETransformComponent& CurrentCameraTransformComponent = Camera->GetComponent(); + std::vector> CurrentFrustum = CurrentCameraComponent.GetFrustumPlanes(); + for (size_t i = 0; i < 6; i++) { - FrustumBufferData[i * 4] = Frustum[i][0]; - FrustumBufferData[i * 4 + 1] = Frustum[i][1]; - FrustumBufferData[i * 4 + 2] = Frustum[i][2]; - FrustumBufferData[i * 4 + 3] = Frustum[i][3]; + FrustumBufferData[i * 4] = CurrentFrustum[i][0]; + FrustumBufferData[i * 4 + 1] = CurrentFrustum[i][1]; + FrustumBufferData[i * 4 + 2] = CurrentFrustum[i][2]; + FrustumBufferData[i * 4 + 3] = CurrentFrustum[i][3]; } - FrustumBufferData[24] = CameraPosition[0]; - FrustumBufferData[25] = CameraPosition[1]; - FrustumBufferData[26] = CameraPosition[2]; + FrustumBufferData[24] = CurrentCameraTransformComponent.GetPosition(FE_WORLD_SPACE)[0]; + FrustumBufferData[25] = CurrentCameraTransformComponent.GetPosition(FE_WORLD_SPACE)[1]; + FrustumBufferData[26] = CurrentCameraTransformComponent.GetPosition(FE_WORLD_SPACE)[2]; FE_GL_ERROR(glUnmapNamedBuffer(FrustumInfoBuffer)); } -void FERenderer::GPUCulling(FEEntityInstanced* Entity, const int SubGameModel, const FEBasicCamera* CurrentCamera) +void FERenderer::GPUCulling(FEEntity* EntityWithInstancedComponent, FEGameModelComponent& GameModelComponent, FEEntity* Camera, size_t PrefabIndex) { if (bFreezeCulling) return; - Entity->CheckDirtyFlag(SubGameModel); + INSTANCED_RENDERING_SYSTEM.CheckDirtyFlag(EntityWithInstancedComponent); + GPUCullingIndividual(EntityWithInstancedComponent, GameModelComponent, Camera, PrefabIndex); +} + +void FERenderer::GPUCullingIndividual(FEEntity* EntityWithInstancedComponent, FEGameModelComponent& GameModelComponent, FEEntity* Camera, size_t BufferIndex) +{ + FEInstancedComponent& InstancedComponent = EntityWithInstancedComponent->GetComponent(); FrustumCullingShader->Start(); + FECameraComponent& CurrentCameraComponent = Camera->GetComponent(); + FETransformComponent& CurrentCameraTransformComponent = Camera->GetComponent(); + #ifdef USE_OCCLUSION_CULLING - FrustumCullingShader->UpdateParameterData("FEProjectionMatrix", CurrentCamera->GetProjectionMatrix()); - FrustumCullingShader->UpdateParameterData("FEViewMatrix", CurrentCamera->GetViewMatrix()); - FrustumCullingShader->UpdateParameterData("useOcclusionCulling", bUseOcclusionCulling); + FrustumCullingShader->UpdateUniformData("FEProjectionMatrix", CurrentCameraComponent.GetProjectionMatrix()); + FrustumCullingShader->UpdateUniformData("FEViewMatrix", CurrentCameraComponent.GetViewMatrix()); + FrustumCullingShader->UpdateUniformData("useOcclusionCulling", bUseOcclusionCulling); // It should be last frame size! - const glm::vec2 RenderTargetSize = glm::vec2(GBuffer->GFrameBuffer->DepthAttachment->GetWidth(), GBuffer->GFrameBuffer->DepthAttachment->GetHeight()); - FrustumCullingShader->UpdateParameterData("renderTargetSize", RenderTargetSize); - FrustumCullingShader->UpdateParameterData("nearFarPlanes", glm::vec2(CurrentCamera->NearPlane, CurrentCamera->FarPlane)); + const glm::vec2 RenderTargetSize = glm::vec2(CurrentCameraRenderingData->GBuffer->GFrameBuffer->DepthAttachment->GetWidth(), CurrentCameraRenderingData->GBuffer->GFrameBuffer->DepthAttachment->GetHeight()); + FrustumCullingShader->UpdateUniformData("renderTargetSize", RenderTargetSize); + FrustumCullingShader->UpdateUniformData("nearFarPlanes", glm::vec2(CurrentCameraComponent.GetNearPlane(), CurrentCameraComponent.GetFarPlane())); #endif // USE_OCCLUSION_CULLING - FrustumCullingShader->LoadDataToGPU(); + FrustumCullingShader->LoadUniformsDataToGPU(); - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, Entity->Renderers[SubGameModel]->SourceDataBuffer)); - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, Entity->Renderers[SubGameModel]->PositionsBuffer)); + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, InstancedComponent.InstancedElementsData[BufferIndex]->SourceDataBuffer)); + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, InstancedComponent.InstancedElementsData[BufferIndex]->PositionsBuffer)); + // TO DO: Check if frustum is updated for each camera. FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, FrustumInfoBuffer)); - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, Entity->Renderers[SubGameModel]->LODBuffers[0])); - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, Entity->Renderers[SubGameModel]->AABBSizesBuffer)); + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[0])); + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, InstancedComponent.InstancedElementsData[BufferIndex]->AABBSizesBuffer)); FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, CullingLODCountersBuffer)); - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, Entity->Renderers[SubGameModel]->LODInfoBuffer)); - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, Entity->Renderers[SubGameModel]->LODBuffers[1])); - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, Entity->Renderers[SubGameModel]->LODBuffers[2])); - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 9, Entity->Renderers[SubGameModel]->LODBuffers[3])); - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, Entity->Renderers[SubGameModel]->IndirectDrawInfoBuffer)); - - DepthPyramid->Bind(0); - /*FE_GL_ERROR(glActiveTexture(GL_TEXTURE0)); - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, depthPyramid));*/ - - FrustumCullingShader->Dispatch(static_cast(ceil(Entity->InstanceCount / 64.0f)), 1, 1); + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, InstancedComponent.InstancedElementsData[BufferIndex]->LODInfoBuffer)); + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[1])); + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[2])); + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 9, InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[3])); + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawInfoBuffer)); + + CurrentCameraRenderingData->DepthPyramid->Bind(0); + FrustumCullingShader->Dispatch(static_cast(ceil(InstancedComponent.InstanceCount / 64.0f)), 1, 1); FE_GL_ERROR(glMemoryBarrier(GL_ALL_BARRIER_BITS)); - - //FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0)); - //FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0)); - //FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, 0)); - //FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, 0)); - //FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, 0)); - //FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, 0)); - //FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, 0)); - //FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, 0)); - //FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, 0)); - //FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 9, 0)); - //FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, 0)); } std::unordered_map> FERenderer::GetDebugOutputTextures() @@ -1834,57 +1896,6 @@ std::unordered_map> FERenderer::GetDeb return DebugOutputTextures; } -void FERenderer::RenderTargetResize(const int NewWidth, const int NewHeight) -{ - delete SceneToTextureFB; - SceneToTextureFB = RESOURCE_MANAGER.CreateFramebuffer(FE_COLOR_ATTACHMENT | FE_DEPTH_ATTACHMENT, NewWidth, NewHeight); - - if (bVRActive) - { - if (VRScreenW != 0 && VRScreenH != 0) - { - delete SceneToVRTextureFB; - SceneToVRTextureFB = RESOURCE_MANAGER.CreateFramebuffer(FE_COLOR_ATTACHMENT | FE_DEPTH_ATTACHMENT, VRScreenW, VRScreenH); - } - } - - if (bSimplifiedRendering) - return; - - delete DepthPyramid; - DepthPyramid = RESOURCE_MANAGER.CreateTexture(); - RESOURCE_MANAGER.Textures.erase(DepthPyramid->GetObjectID()); - - DepthPyramid->Bind(); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); - - const int MaxDimention = std::max(NewWidth, NewHeight); - const size_t MipCount = static_cast(floor(log2(MaxDimention)) + 1); - FE_GL_ERROR(glTexStorage2D(GL_TEXTURE_2D, static_cast(MipCount), GL_R32F, NewWidth, NewHeight)); - DepthPyramid->Width = NewWidth; - DepthPyramid->Height = NewHeight; - - GBuffer->RenderTargetResize(SceneToTextureFB); - SSAO->RenderTargetResize(SceneToTextureFB); - - for (size_t i = 0; i < PostProcessEffects.size(); i++) - { - // We should delete only internally created frame buffers. - // Other wise user created postProcess could create UB. - if (PostProcessEffects[i]->GetName() == "bloom" || - PostProcessEffects[i]->GetName() == "GammaAndHDR" || - PostProcessEffects[i]->GetName() == "FE_FXAA" || - PostProcessEffects[i]->GetName() == "DOF" || - PostProcessEffects[i]->GetName() == "chromaticAberration") - delete PostProcessEffects[i]; - } - - PostProcessEffects.clear(); -} - bool FERenderer::IsOcclusionCullingEnabled() { return bUseOcclusionCulling; @@ -1895,26 +1906,30 @@ void FERenderer::SetOcclusionCullingEnabled(const bool NewValue) bUseOcclusionCulling = NewValue; } -void FERenderer::UpdateSSAO(const FEBasicCamera* CurrentCamera) +void FERenderer::UpdateSSAO() { - if (!SSAO->bActive) + if (CurrentCameraRenderingData == nullptr) return; - SSAO->FB->Bind(); - if (SSAO->Shader == nullptr) - SSAO->Shader = RESOURCE_MANAGER.GetShader("1037115B676E383E36345079"/*"FESSAOShader"*/); + FECameraComponent& CameraComponent = CurrentCameraRenderingData->CameraEntity->GetComponent(); + if (!CameraComponent.IsSSAOEnabled()) + return; + + CurrentCameraRenderingData->SSAO->FB->Bind(); + if (CurrentCameraRenderingData->SSAO->Shader == nullptr) + CurrentCameraRenderingData->SSAO->Shader = RESOURCE_MANAGER.GetShader("1037115B676E383E36345079"/*"FESSAOShader"*/); - SSAO->Shader->UpdateParameterData("SampleCount", SSAO->SampleCount); + CurrentCameraRenderingData->SSAO->Shader->UpdateUniformData("SampleCount", CameraComponent.GetSSAOSampleCount()); - SSAO->Shader->UpdateParameterData("SmallDetails", SSAO->bSmallDetails ? 1.0f : 0.0f); - SSAO->Shader->UpdateParameterData("Bias", SSAO->Bias); - SSAO->Shader->UpdateParameterData("Radius", SSAO->Radius); - SSAO->Shader->UpdateParameterData("RadiusSmallDetails", SSAO->RadiusSmallDetails); - SSAO->Shader->UpdateParameterData("SmallDetailsWeight", SSAO->SmallDetailsWeight); + CurrentCameraRenderingData->SSAO->Shader->UpdateUniformData("SmallDetails", CameraComponent.IsSSAOSmallDetailsEnabled() ? 1.0f : 0.0f); + CurrentCameraRenderingData->SSAO->Shader->UpdateUniformData("Bias", CameraComponent.GetSSAOBias()); + CurrentCameraRenderingData->SSAO->Shader->UpdateUniformData("Radius", CameraComponent.GetSSAORadius()); + CurrentCameraRenderingData->SSAO->Shader->UpdateUniformData("RadiusSmallDetails", CameraComponent.GetSSAORadiusSmallDetails()); + CurrentCameraRenderingData->SSAO->Shader->UpdateUniformData("SmallDetailsWeight", CameraComponent.GetSSAOSmallDetailsWeight()); - SSAO->Shader->Start(); - LoadStandardParams(SSAO->Shader, CurrentCamera, true); - SSAO->Shader->LoadDataToGPU(); + CurrentCameraRenderingData->SSAO->Shader->Start(); + LoadStandardUniforms(CurrentCameraRenderingData->SSAO->Shader, true, CurrentCameraRenderingData->CameraEntity); + CurrentCameraRenderingData->SSAO->Shader->LoadUniformsDataToGPU(); FE_GL_ERROR(glBindVertexArray(RESOURCE_MANAGER.GetMesh("1Y251E6E6T78013635793156"/*"plane"*/)->GetVaoID())); FE_GL_ERROR(glEnableVertexAttribArray(0)); @@ -1922,23 +1937,23 @@ void FERenderer::UpdateSSAO(const FEBasicCamera* CurrentCamera) FE_GL_ERROR(glDisableVertexAttribArray(0)); FE_GL_ERROR(glBindVertexArray(0)); - SSAO->Shader->Stop(); + CurrentCameraRenderingData->SSAO->Shader->Stop(); - if (SSAO->bBlured) + if (CameraComponent.IsSSAOResultBlured()) { // First blur stage FEShader* BlurShader = RESOURCE_MANAGER.GetShader("0B5770660B6970800D776542"/*"FESSAOBlurShader"*/); BlurShader->Start(); - if (BlurShader->GetParameter("FEBlurDirection")) - BlurShader->UpdateParameterData("FEBlurDirection", glm::vec2(0.0f, 1.0f)); - if (BlurShader->GetParameter("BlurRadius")) - BlurShader->UpdateParameterData("BlurRadius", 1.3f); + if (BlurShader->GetUniform("FEBlurDirection")) + BlurShader->UpdateUniformData("FEBlurDirection", glm::vec2(0.0f, 1.0f)); + if (BlurShader->GetUniform("BlurRadius")) + BlurShader->UpdateUniformData("BlurRadius", 1.3f); - BlurShader->LoadDataToGPU(); + BlurShader->LoadUniformsDataToGPU(); - SSAO->FB->GetColorAttachment()->Bind(0); - SceneToTextureFB->GetDepthAttachment()->Bind(1); - GBuffer->Normals->Bind(2); + CurrentCameraRenderingData->SSAO->FB->GetColorAttachment()->Bind(0); + CurrentCameraRenderingData->SceneToTextureFB->GetDepthAttachment()->Bind(1); + CurrentCameraRenderingData->GBuffer->Normals->Bind(2); FE_GL_ERROR(glBindVertexArray(RESOURCE_MANAGER.GetMesh("1Y251E6E6T78013635793156"/*"plane"*/)->GetVaoID())); FE_GL_ERROR(glEnableVertexAttribArray(0)); FE_GL_ERROR(glDrawElements(GL_TRIANGLES, RESOURCE_MANAGER.GetMesh("1Y251E6E6T78013635793156"/*"plane"*/)->GetVertexCount(), GL_UNSIGNED_INT, 0)); @@ -1946,16 +1961,16 @@ void FERenderer::UpdateSSAO(const FEBasicCamera* CurrentCamera) FE_GL_ERROR(glBindVertexArray(0)); // Second blur stage - if (BlurShader->GetParameter("FEBlurDirection")) - BlurShader->UpdateParameterData("FEBlurDirection", glm::vec2(1.0f, 0.0f)); - if (BlurShader->GetParameter("BlurRadius")) - BlurShader->UpdateParameterData("BlurRadius", 1.3f); + if (BlurShader->GetUniform("FEBlurDirection")) + BlurShader->UpdateUniformData("FEBlurDirection", glm::vec2(1.0f, 0.0f)); + if (BlurShader->GetUniform("BlurRadius")) + BlurShader->UpdateUniformData("BlurRadius", 1.3f); - BlurShader->LoadDataToGPU(); + BlurShader->LoadUniformsDataToGPU(); - SSAO->FB->GetColorAttachment()->Bind(0); - SceneToTextureFB->GetDepthAttachment()->Bind(1); - GBuffer->Normals->Bind(2); + CurrentCameraRenderingData->SSAO->FB->GetColorAttachment()->Bind(0); + CurrentCameraRenderingData->SceneToTextureFB->GetDepthAttachment()->Bind(1); + CurrentCameraRenderingData->GBuffer->Normals->Bind(2); FE_GL_ERROR(glBindVertexArray(RESOURCE_MANAGER.GetMesh("1Y251E6E6T78013635793156"/*"plane"*/)->GetVaoID())); FE_GL_ERROR(glEnableVertexAttribArray(0)); FE_GL_ERROR(glDrawElements(GL_TRIANGLES, RESOURCE_MANAGER.GetMesh("1Y251E6E6T78013635793156"/*"plane"*/)->GetVertexCount(), GL_UNSIGNED_INT, 0)); @@ -1964,97 +1979,11 @@ void FERenderer::UpdateSSAO(const FEBasicCamera* CurrentCamera) BlurShader->Stop(); - SSAO->FB->GetColorAttachment()->UnBind(); - SSAO->FB->UnBind(); + CurrentCameraRenderingData->SSAO->FB->GetColorAttachment()->UnBind(); + CurrentCameraRenderingData->SSAO->FB->UnBind(); } } -bool FERenderer::IsSSAOEnabled() -{ - return SSAO->bActive; -} - -void FERenderer::SetSSAOEnabled(const bool NewValue) -{ - SSAO->bActive = NewValue; -} - -int FERenderer::GetSSAOSampleCount() -{ - return SSAO->SampleCount; -} - -void FERenderer::SetSSAOSampleCount(int NewValue) -{ - if (NewValue < 1) - NewValue = 1; - - if (NewValue > 64) - NewValue = 64; - - SSAO->SampleCount = NewValue; -} - -bool FERenderer::IsSSAOSmallDetailsEnabled() -{ - return SSAO->bSmallDetails; -} - -void FERenderer::SetSSAOSmallDetailsEnabled(const bool NewValue) -{ - SSAO->bSmallDetails = NewValue; -} - -bool FERenderer::IsSSAOResultBlured() -{ - return SSAO->bBlured; -} - -void FERenderer::SetSSAOResultBlured(const bool NewValue) -{ - SSAO->bBlured = NewValue; -} - -float FERenderer::GetSSAOBias() -{ - return SSAO->Bias; -} - -void FERenderer::SetSSAOBias(const float NewValue) -{ - SSAO->Bias = NewValue; -} - -float FERenderer::GetSSAORadius() -{ - return SSAO->Radius; -} - -void FERenderer::SetSSAORadius(const float NewValue) -{ - SSAO->Radius = NewValue; -} - -float FERenderer::GetSSAORadiusSmallDetails() -{ - return SSAO->RadiusSmallDetails; -} - -void FERenderer::SetSSAORadiusSmallDetails(const float NewValue) -{ - SSAO->RadiusSmallDetails = NewValue; -} - -float FERenderer::GetSSAOSmallDetailsWeight() -{ - return SSAO->SmallDetailsWeight; -} - -void FERenderer::SetSSAOSmallDetailsWeight(const float NewValue) -{ - SSAO->SmallDetailsWeight = NewValue; -} - void FERenderer::InitVR(int VRScreenW, int VRScreenH) { UpdateVRRenderTargetSize( VRScreenW, VRScreenH); @@ -2072,45 +2001,38 @@ void FERenderer::UpdateVRRenderTargetSize(int VRScreenW, int VRScreenH) } } -void FERenderer::RenderVR(FEBasicCamera* CurrentCamera/*, uint32_t ColorTexture, uint32_t DepthTexture*/) +void FERenderer::RenderVR(FEScene* CurrentScene) { - if (SceneToVRTextureFB == nullptr) + if (CurrentScene == nullptr || SceneToVRTextureFB == nullptr) return; - // glViewPort and Frame buffer already are set. - CurrentCamera->UpdateFrustumPlanes(); - FEScene& scene = SCENE; + FEEntity* MainCameraEntity = CAMERA_SYSTEM.GetMainCamera(CurrentScene); + if (MainCameraEntity == nullptr) + return; SceneToVRTextureFB->Bind(); glClearColor(0.55f, 0.73f, 0.87f, 1.0f); FE_GL_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); - LoadUniformBlocks(); + LoadUniformBlocks(CurrentScene); - auto EntityIterator = scene.EntityMap.begin(); - while (EntityIterator != scene.EntityMap.end()) + // No instanced rendering for now. + entt::basic_group GameModelGroup = CurrentScene->Registry.group(entt::get, entt::exclude); + for (entt::entity CurrentEnTTEntity : GameModelGroup) { - auto entity = EntityIterator->second; + auto& [GameModelComponent, TransformComponent] = GameModelGroup.get(CurrentEnTTEntity); + + FEEntity* CurrentEntity = CurrentScene->GetEntityByEnTT(CurrentEnTTEntity); + if (CurrentEntity == nullptr) + continue; - if (entity->IsVisible() && entity->IsPostprocessApplied()) + if (GameModelComponent.IsVisible() && GameModelComponent.IsPostprocessApplied()) { - if (entity->GetType() == FE_ENTITY) - { - //ForceShader(RESOURCE_MANAGER.GetShader("670B01496E202658377A4576"/*"FEPBRGBufferShader"*/)); - RenderEntityForward(entity, CurrentCamera); - } - else if (entity->GetType() == FE_ENTITY_INSTANCED) - { - //ForceShader(RESOURCE_MANAGER.GetShader("613830232E12602D6A1D2C17"/*"FEPBRInstancedGBufferShader"*/)); - //RenderEntityInstanced(reinterpret_cast(entity), CurrentCamera, CurrentCamera->GetFrustumPlanes(), false); - } + RenderGameModelComponentForward(CurrentEntity, MainCameraEntity); } - - EntityIterator++; } SceneToVRTextureFB->UnBind(); - LastRenderedResult = SceneToVRTextureFB; for (size_t i = 0; i < AfterRenderCallbacks.size(); i++) { @@ -2145,7 +2067,7 @@ void FERenderer::RenderToFrameBuffer(FETexture* SceneTexture, GLuint Target) FEShader* ScreenQuadShader = RESOURCE_MANAGER.GetShader("7933272551311F3A1A5B2363"/*"FEScreenQuadShader"*/); ScreenQuadShader->Start(); - ScreenQuadShader->LoadDataToGPU(); + ScreenQuadShader->LoadUniformsDataToGPU(); FE_GL_ERROR(glBindVertexArray(RESOURCE_MANAGER.GetMesh("1Y251E6E6T78013635793156"/*"plane"*/)->GetVaoID())); FE_GL_ERROR(glEnableVertexAttribArray(0)); @@ -2230,7 +2152,7 @@ bool FERenderer::CombineFrameBuffers(FEFramebuffer* FirstSource, FEFramebuffer* FEShader* CurrentShader = RESOURCE_MANAGER.GetShader("5C267A01466A545E7D1A2E66"/*FECombineFrameBuffers*/); CurrentShader->Start(); - CurrentShader->LoadDataToGPU(); + CurrentShader->LoadUniformsDataToGPU(); FE_GL_ERROR(glBindVertexArray(RESOURCE_MANAGER.GetMesh("1Y251E6E6T78013635793156"/*"plane"*/)->GetVaoID())); FE_GL_ERROR(glEnableVertexAttribArray(0)); @@ -2265,19 +2187,21 @@ void FERenderer::AddAfterRenderCallback(std::function Callback) AfterRenderCallbacks.push_back(Callback); } -bool FERenderer::IsClearActiveInSimplifiedRendering() +void FERenderer::SetGLViewport(int X, int Y, int Width, int Height) { - return bClearActiveInSimplifiedRendering; + SetGLViewport(glm::ivec4(X, Y, Width, Height)); } -void FERenderer::SetClearActiveInSimplifiedRendering(bool NewValue) +void FERenderer::SetGLViewport(glm::ivec4 ViewPortData) { - bClearActiveInSimplifiedRendering = NewValue; + FE_GL_ERROR(glViewport(ViewPortData.x, ViewPortData.y, ViewPortData.z, ViewPortData.w)); } -FEFramebuffer* FERenderer::GetLastRenderedResult() +glm::ivec4 FERenderer::GetGLViewport() { - return LastRenderedResult; + glm::ivec4 Viewport; + FE_GL_ERROR(glGetIntegerv(GL_VIEWPORT, &Viewport[0])); + return Viewport; } void FEGBuffer::InitializeResources(FEFramebuffer* MainFrameBuffer) @@ -2298,6 +2222,9 @@ void FEGBuffer::InitializeResources(FEFramebuffer* MainFrameBuffer) ShaderProperties = RESOURCE_MANAGER.CreateTexture(GL_RGBA, GL_RGBA, MainFrameBuffer->GetColorAttachment()->GetWidth(), MainFrameBuffer->GetColorAttachment()->GetHeight()); GFrameBuffer->SetColorAttachment(ShaderProperties, 5); + + MotionVectors = RESOURCE_MANAGER.CreateTexture(GL_RG16F, GL_RG, MainFrameBuffer->GetColorAttachment()->GetWidth(), MainFrameBuffer->GetColorAttachment()->GetHeight()); + GFrameBuffer->SetColorAttachment(MotionVectors, 6); } FEGBuffer::FEGBuffer(FEFramebuffer* MainFrameBuffer) diff --git a/Renderer/FERenderer.h b/Renderer/FERenderer.h index 7fac276..dfea6db 100644 --- a/Renderer/FERenderer.h +++ b/Renderer/FERenderer.h @@ -3,7 +3,8 @@ #ifndef FERENDERER_H #define FERENDERER_H -#include "../SubSystems/Scene/FEScene.h" +#include "../SubSystems/Scene/Components/Systems/FEComponentSystems.h" +#include "../SubSystems/FEInput.h" namespace FocalEngine { @@ -27,6 +28,7 @@ namespace FocalEngine FETexture* Albedo = nullptr; FETexture* MaterialProperties = nullptr; FETexture* ShaderProperties = nullptr; + FETexture* MotionVectors = nullptr; void RenderTargetResize(FEFramebuffer* MainFrameBuffer); }; @@ -41,91 +43,48 @@ namespace FocalEngine FEFramebuffer* FB = nullptr; FEShader* Shader = nullptr; + }; - bool bActive = true; - int SampleCount = 16; + // TO-DO: That is very non GPU memory efficient way to support multiple cameras. + struct FECameraRenderingData + { + friend class FERenderer; + FEEntity* CameraEntity = nullptr; + FEFramebuffer* SceneToTextureFB = nullptr; + FEGBuffer* GBuffer = nullptr; + FESSAO* SSAO = nullptr; + FETexture* DepthPyramid = nullptr; + std::vector PostProcessEffects; - bool bSmallDetails = true; - bool bBlured = true; + FETexture* FinalScene = nullptr; - float Bias = 0.013f; - float Radius = 10.0f; - float RadiusSmallDetails = 0.4f; - float SmallDetailsWeight = 0.2f; + ~FECameraRenderingData() + { + delete SceneToTextureFB; + delete GBuffer; + delete SSAO; + delete DepthPyramid; + } }; - class FERenderer + class FOCAL_ENGINE_API FERenderer { friend FEngine; + friend FECameraSystem; public: SINGLETON_PUBLIC_PART(FERenderer) - void Render(FEBasicCamera* CurrentCamera); - void RenderEntity(const FEEntity* Entity, const FEBasicCamera* CurrentCamera, bool bReloadUniformBlocks = false, int ComponentIndex = -1); - void RenderEntityForward(const FEEntity* Entity, const FEBasicCamera* CurrentCamera, bool bReloadUniformBlocks = false); - void RenderEntityInstanced(FEEntityInstanced* EntityInstanced, FEBasicCamera* CurrentCamera, float** Frustum, bool bShadowMap = false, bool bReloadUniformBlocks = false, int ComponentIndex = -1); - void RenderTerrain(FETerrain* Terrain, const FEBasicCamera* CurrentCamera); - void AddPostProcess(FEPostProcess* NewPostProcess, bool NoProcessing = false); - - std::vector GetPostProcessList(); - FEPostProcess* GetPostProcessEffect(std::string ID); - - //#fix postProcess - FEFramebuffer* SceneToTextureFB = nullptr; - FETexture* FinalScene = nullptr; - std::vector PostProcessEffects; + void Render(FEScene* CurrentScene); + + void RenderGameModelComponent(FEEntity* Entity, FEEntity* Camera, bool bReloadUniformBlocks = false); + void RenderGameModelComponent(FEGameModelComponent& GameModelComponent, FETransformComponent& TransformComponent, FEScene* ParentScene, FEEntity* Camera, bool bReloadUniformBlocks = false); + void RenderGameModelComponentForward(FEEntity* Entity, FEEntity* Camera, bool bReloadUniformBlocks = false); + void RenderGameModelComponentWithInstanced(FEEntity* Entity, FEEntity* Camera, bool bShadowMap = false, bool bReloadUniformBlocks = false, size_t PrefabIndex = 0); + void RenderTerrainComponent(FEEntity* TerrainEntity, FEEntity* Camera); + + FETexture* GetCameraResult(FEEntity* CameraEntity); void DrawLine(glm::vec3 BeginPoint, glm::vec3 EndPoint, glm::vec3 Color = glm::vec3(1.0f), float Width = 0.1f); - // *********** Anti-Aliasing(FXAA) *********** - float GetFXAASpanMax(); - void SetFXAASpanMax(float NewValue); - - float GetFXAAReduceMin(); - void SetFXAAReduceMin(float NewValue); - - float GetFXAAReduceMul(); - void SetFXAAReduceMul(float NewValue); - - // *********** Bloom *********** - float GetBloomThreshold(); - void SetBloomThreshold(float NewValue); - - float GetBloomSize(); - void SetBloomSize(float NewValue); - - // *********** Depth of Field *********** - float GetDOFNearDistance(); - void SetDOFNearDistance(float NewValue); - - float GetDOFFarDistance(); - void SetDOFFarDistance(float NewValue); - - float GetDOFStrength(); - void SetDOFStrength(float NewValue); - - float GetDOFDistanceDependentStrength(); - void SetDOFDistanceDependentStrength(float NewValue); - - // *********** Chromatic Aberration *********** - float GetChromaticAberrationIntensity(); - void SetChromaticAberrationIntensity(float NewValue); - - // *********** Distance fog *********** - bool IsDistanceFogEnabled(); - void SetDistanceFogEnabled(bool NewValue); - - float GetDistanceFogDensity(); - void SetDistanceFogDensity(float NewValue); - - float GetDistanceFogGradient(); - void SetDistanceFogGradient(float NewValue); - - // *********** Sky *********** - bool IsSkyEnabled(); - void SetSkyEnabled(bool NewValue); - - float GetDistanceToSky(); - void SetDistanceToSky(float NewValue); float TestTime = 0.0f; float LastTestTime = 0.0f; @@ -136,37 +95,10 @@ namespace FocalEngine bool IsOcclusionCullingEnabled(); void SetOcclusionCullingEnabled(bool NewValue); - // *********** SSAO *********** - bool IsSSAOEnabled(); - void SetSSAOEnabled(bool NewValue); - - int GetSSAOSampleCount(); - void SetSSAOSampleCount(int NewValue); - - bool IsSSAOSmallDetailsEnabled(); - void SetSSAOSmallDetailsEnabled(bool NewValue); - - bool IsSSAOResultBlured(); - void SetSSAOResultBlured(bool NewValue); - - float GetSSAOBias(); - void SetSSAOBias(float NewValue); - - float GetSSAORadius(); - void SetSSAORadius(float NewValue); - - float GetSSAORadiusSmallDetails(); - void SetSSAORadiusSmallDetails(float NewValue); - - float GetSSAOSmallDetailsWeight(); - void SetSSAOSmallDetailsWeight(float NewValue); - - FEGBuffer* GBuffer = nullptr; - FESSAO* SSAO; - void UpdateSSAO(const FEBasicCamera* CurrentCamera); + void UpdateSSAO(); std::unordered_map> GetDebugOutputTextures(); - void SimplifiedRender(FEBasicCamera* CurrentCamera); + void SimplifiedRender(FEScene* CurrentScene); // *********** VR Rendering *********** @@ -175,7 +107,7 @@ namespace FocalEngine int VRScreenW = 0; int VRScreenH = 0; FEFramebuffer* SceneToVRTextureFB = nullptr; - void RenderVR(FEBasicCamera* CurrentCamera/*, uint32_t ColorTexture, uint32_t DepthTexture*/); + void RenderVR(FEScene* CurrentScene); void UpdateVRRenderTargetSize(int VRScreenW, int VRScreenH); @@ -187,19 +119,21 @@ namespace FocalEngine bool CombineFrameBuffers(FEFramebuffer* FirstSource, FEFramebuffer* SecondSource, FEFramebuffer* Target); void AddAfterRenderCallback(std::function Callback); - FEFramebuffer* GetLastRenderedResult(); - bool IsClearActiveInSimplifiedRendering(); - void SetClearActiveInSimplifiedRendering(bool NewValue); + void SetGLViewport(int X, int Y, int Width, int Height); + void SetGLViewport(glm::ivec4 ViewPortData); + glm::ivec4 GetGLViewport(); + + FECameraRenderingData* GetCameraRenderingData(FEEntity* CameraEntity); + void AddPostProcess(FECameraRenderingData* CameraRenderingData, FEPostProcess* NewPostProcess, const bool NoProcessing = false); private: SINGLETON_PRIVATE_PART(FERenderer) - void LoadStandardParams(FEShader* Shader, const FEBasicCamera* CurrentCamera, FEMaterial* Material, const FETransformComponent* Transform, bool IsReceivingShadows = false, const bool IsUniformLighting = false); - void LoadStandardParams(FEShader* Shader, const FEBasicCamera* CurrentCamera, bool IsReceivingShadows, const bool IsUniformLighting = false); - void LoadUniformBlocks(); + void LoadStandardUniforms(FEShader* Shader, FEMaterial* Material, FETransformComponent* Transform, FEEntity* Camera, bool IsReceivingShadows = false, const bool IsUniformLighting = false); + void LoadStandardUniforms(FEShader* Shader, bool IsReceivingShadows, FEEntity* Camera, const bool IsUniformLighting = false); + void LoadUniformBlocks(FEScene* CurrentScene); - void StandardFBInit(int WindowWidth, int WindowHeight); - void TakeScreenshot(const char* FileName, int Width, int Height); + void SaveScreenshot(std::string FileName, FEScene* SceneToWorkWith); FEMaterial* ShadowMapMaterial; FEMaterial* ShadowMapMaterialInstanced; @@ -223,18 +157,8 @@ namespace FocalEngine GLuint InstancedLineVAO = 0; GLenum InstancedLineBuffer = 0; - FEBasicCamera* EngineMainCamera = nullptr; glm::dvec3 MouseRay = glm::dvec3(0.0); - void UpdateTerrainBrush(FETerrain* Terrain); - - FEEntity* SkyDome = nullptr; - - float DistanceFogDensity = 0.007f; - float DistanceFogGradient = 2.5f; - bool bDistanceFogEnabled = false; - void UpdateFogInShaders(); - FEShader* ShaderToForce = nullptr; void ForceShader(FEShader* Shader); @@ -246,31 +170,33 @@ namespace FocalEngine GLuint FrustumInfoBuffer = 0; GLuint CullingLODCountersBuffer = 0; - void UpdateGPUCullingFrustum(float** Frustum, glm::vec3 CameraPosition); - void GPUCulling(FEEntityInstanced* Entity, int SubGameModel, const FEBasicCamera* CurrentCamera); + void UpdateGPUCullingFrustum(FEEntity* Camera); + void GPUCulling(FEEntity* EntityWithInstancedComponent, FEGameModelComponent& GameModelComponent, FEEntity* Camera, size_t PrefabIndex = 0); + void GPUCullingIndividual(FEEntity* EntityWithInstancedComponent, FEGameModelComponent& GameModelComponent, FEEntity* Camera, size_t BufferIndex); - FETexture* DepthPyramid = nullptr; bool bUseOcclusionCulling = true; // *********** GPU Culling END *********** std::unordered_map> DebugOutputTextures; - void RenderTargetResize(int NewWidth, int NewHeight); - - bool bSimplifiedRendering = false; void Init(); - // *********** VR Rendering *********** - - bool bClearActiveInSimplifiedRendering = true; - - // *********** VR Rendering END *********** - - FEFramebuffer* LastRenderedResult = nullptr; std::vector> AfterRenderCallbacks; + + std::unordered_map CameraRenderingDataMap; + FECameraRenderingData* CreateCameraRenderingData(FEEntity* CameraEntity); + void ForceCameraRenderingDataUpdate(FEEntity* CameraEntity); + + FECameraRenderingData* CurrentCameraRenderingData = nullptr; + void UpdateShadersForCamera(FECameraRenderingData* CameraData); }; - #define RENDERER FERenderer::getInstance() +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetRenderer(); + #define RENDERER (*static_cast(GetRenderer())) +#else + #define RENDERER FERenderer::GetInstance() +#endif } #endif // FERENDERER_H \ No newline at end of file diff --git a/Renderer/FEShader.cpp b/Renderer/FEShader.cpp index 6208a56..7f97596 100644 --- a/Renderer/FEShader.cpp +++ b/Renderer/FEShader.cpp @@ -1,205 +1,6 @@ #include "FEShader.h" using namespace FocalEngine; -FEShaderParam::FEShaderParam() -{ - Data = nullptr; -} - -std::string FEShaderParam::GetName() -{ - return Name; -} - -void FEShaderParam::SetName(const std::string NewName) -{ - Name = NewName; - NameHash = static_cast(std::hash{}(Name)); -} - -FEShaderParam::FEShaderParam(const bool Data, const std::string Name) -{ - this->Data = new bool(Data); - Type = FE_BOOL_UNIFORM; - this->Name = Name; - NameHash = static_cast(std::hash{}(Name)); -} - -FEShaderParam::FEShaderParam(const int Data, const std::string Name) -{ - this->Data = new int(Data); - Type = FE_INT_SCALAR_UNIFORM; - this->Name = Name; - NameHash = static_cast(std::hash{}(Name)); -} - -FEShaderParam::FEShaderParam(const float Data, const std::string Name) -{ - this->Data = new float(Data); - Type = FE_FLOAT_SCALAR_UNIFORM; - this->Name = Name; - NameHash = static_cast(std::hash{}(Name)); -} - -FEShaderParam::FEShaderParam(const glm::vec2 Data, const std::string Name) -{ - this->Data = new glm::vec2(Data); - Type = FE_VECTOR2_UNIFORM; - this->Name = Name; - NameHash = static_cast(std::hash{}(Name)); -} - -FEShaderParam::FEShaderParam(const glm::vec3 Data, const std::string Name) -{ - this->Data = new glm::vec3(Data); - Type = FE_VECTOR3_UNIFORM; - this->Name = Name; - NameHash = static_cast(std::hash{}(Name)); -} - -FEShaderParam::FEShaderParam(const glm::vec4 Data, const std::string Name) -{ - this->Data = new glm::vec4(Data); - Type = FE_VECTOR4_UNIFORM; - this->Name = Name; - NameHash = static_cast(std::hash{}(Name)); -} - -FEShaderParam::FEShaderParam(const glm::mat4 Data, const std::string Name) -{ - this->Data = new glm::mat4(Data); - Type = FE_MAT4_UNIFORM; - this->Name = Name; - NameHash = static_cast(std::hash{}(Name)); -} - -void FEShaderParam::CopyCode(const FEShaderParam& Copy) -{ - switch (Copy.Type) - { - case FE_BOOL_UNIFORM: - { - Data = new bool; - *static_cast(Data) = *static_cast(Copy.Data); - break; - } - - case FE_INT_SCALAR_UNIFORM: - { - Data = new int; - *static_cast(Data) = *static_cast(Copy.Data); - break; - } - - case FE_FLOAT_SCALAR_UNIFORM: - { - Data = new float; - *static_cast(Data) = *static_cast(Copy.Data); - break; - } - - case FE_VECTOR2_UNIFORM: - { - Data = new glm::vec2; - *static_cast(Data) = *static_cast(Copy.Data); - break; - } - - case FE_VECTOR3_UNIFORM: - { - Data = new glm::vec3; - *static_cast(Data) = *static_cast(Copy.Data); - break; - } - - case FE_VECTOR4_UNIFORM: - { - Data = new glm::vec4; - *static_cast(Data) = *static_cast(Copy.Data); - break; - } - - case FE_MAT4_UNIFORM: - { - Data = new glm::mat4; - *static_cast(Data) = *static_cast(Copy.Data); - break; - } - - default: - break; - } -} - -FEShaderParam::FEShaderParam(const FEShaderParam& Copy) -{ - this->Type = Copy.Type; - this->Name = Copy.Name; - this->NameHash = Copy.NameHash; - - CopyCode(Copy); -} - -void FEShaderParam::operator=(const FEShaderParam& Assign) -{ - if (&Assign != this) - this->~FEShaderParam(); - - this->Type = Assign.Type; - this->Name = Assign.Name; - this->NameHash = Assign.NameHash; - - CopyCode(Assign); -} - -FEShaderParam::~FEShaderParam() -{ - if (Data == nullptr) - return; - - switch (Type) - { - case FE_INT_SCALAR_UNIFORM: - { - delete static_cast(Data); - break; - } - - case FE_FLOAT_SCALAR_UNIFORM: - { - delete static_cast(Data); - break; - } - - case FE_VECTOR2_UNIFORM: - { - delete static_cast(Data); - break; - } - - case FE_VECTOR3_UNIFORM: - { - delete static_cast(Data); - break; - } - - case FE_VECTOR4_UNIFORM: - { - delete static_cast(Data); - break; - } - - case FE_MAT4_UNIFORM: - { - delete static_cast(Data); - break; - } - - default: - break; - } -} - FEShader::FEShader(const std::string Name, const char* VertexText, const char* FragmentText, const char* TessControlText, const char* TessEvalText, const char* GeometryText, const char* ComputeText, const bool TestCompilation, const int GlslVersion) : FEObject(FE_SHADER, Name) @@ -292,6 +93,7 @@ FEShader::FEShader(const std::string Name, const char* VertexText, const char* F if (ComputeText != nullptr) FE_GL_ERROR(glDeleteShader(ComputeShaderID)); + RegisterActiveAttributes(); RegisterUniforms(); #ifdef FE_DEBUG_ENABLED @@ -349,9 +151,8 @@ void FEShader::CopyCode(const FEShader& Shader) VertexAttributes = Shader.VertexAttributes; - Parameters = Shader.Parameters; + Uniforms = Shader.Uniforms; BlockUniforms = Shader.BlockUniforms; - UniformLocations = Shader.UniformLocations; TextureUniforms = Shader.TextureUniforms; bCSM = Shader.bCSM; @@ -385,68 +186,276 @@ FEShader::~FEShader() void FEShader::RegisterUniforms() { - GLint Count; - GLint Size; - GLenum Type; - - GLsizei Length; + GLint UniformCount; + GLint UniformSize; + GLenum UniformType; + GLsizei ActualUniformNameLength; - FE_GL_ERROR(glGetProgramiv(ProgramID, GL_ACTIVE_UNIFORMS, &Count)); - for (size_t i = 0; i < static_cast(Count); i++) + FE_GL_ERROR(glGetProgramiv(ProgramID, GL_ACTIVE_UNIFORMS, &UniformCount)); + for (size_t i = 0; i < static_cast(UniformCount); i++) { - const GLsizei BufSize = 64; - GLchar name[BufSize]; - FE_GL_ERROR(glGetActiveUniform(ProgramID, static_cast(i), BufSize, &Length, &Size, &Type, name)); - // arrays are not currently part of params - if (std::string(name).find("[") != std::string::npos) - continue; + const GLsizei MaxUniformNameLength = 512; + GLchar UniformName[MaxUniformNameLength]; + FE_GL_ERROR(glGetActiveUniform(ProgramID, static_cast(i), MaxUniformNameLength, &ActualUniformNameLength, &UniformSize, &UniformType, UniformName)); + + std::string UniformNameString = UniformName; + if (UniformNameString.find("debugFlag") != std::string::npos) + { + int y = 0; + y++; + //bDebugRequest = true; + //continue; + } + + bool bIsArray = ((UniformSize > 1) && (UniformNameString.find('[') != std::string::npos)); + std::string UniformArrayName = ""; + if (bIsArray) + UniformArrayName = UniformNameString.substr(0, UniformNameString.find('[')); + + std::vector Locations; + if (bIsArray) + { + for (size_t i = 0; i < UniformSize; i++) + { + std::string CurrentVariableName = UniformArrayName + "[" + std::to_string(i) + "]"; + GLuint Location = -1; + FE_GL_ERROR(Location = glGetUniformLocation(ProgramID, CurrentVariableName.c_str())); + Locations.push_back(Location); + } + } + else + { + GLuint Location = -1; + FE_GL_ERROR(Location = glGetUniformLocation(ProgramID, UniformName)); + Locations.push_back(Location); + } - switch (Type) + switch (UniformType) { case GL_BOOL: { - AddParameter(FEShaderParam(false, name)); + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_BOOL_ARRAY, std::vector(UniformSize), UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_BOOL, false, UniformName, Locations)); + } + + break; + } + + case GL_UNSIGNED_INT: + { + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_UNSIGNED_INT_ARRAY, std::vector(UniformSize), UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_UNSIGNED_INT, 0, UniformName, Locations)); + } + break; } case GL_INT: { - AddParameter(FEShaderParam(0, name)); + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_INT_ARRAY, std::vector(UniformSize), UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_INT, 0, UniformName, Locations)); + } + break; } case GL_FLOAT: { - AddParameter(FEShaderParam(0.0f, name)); + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_FLOAT_ARRAY, std::vector(UniformSize), UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_FLOAT, 0.0f, UniformName, Locations)); + } + break; } case GL_FLOAT_VEC2: { - AddParameter(FEShaderParam(glm::vec2(0.0f), name)); + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR2_ARRAY, std::vector(UniformSize), UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR2, glm::vec2(0.0f), UniformName, Locations)); + } + break; } case GL_FLOAT_VEC3: { - AddParameter(FEShaderParam(glm::vec3(0.0f), name)); + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR3_ARRAY, std::vector(UniformSize), UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR3, glm::vec3(0.0f), UniformName, Locations)); + } + break; } case GL_FLOAT_VEC4: { - AddParameter(FEShaderParam(glm::vec4(0.0f), name)); + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR4_ARRAY, std::vector(UniformSize), UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR4, glm::vec4(0.0f), UniformName, Locations)); + } + break; } case GL_FLOAT_MAT4: { - AddParameter(FEShaderParam(glm::mat4(1.0f), name)); + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_MAT4_ARRAY, std::vector(UniformSize), UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_MAT4, glm::mat4(1.0f), UniformName, Locations)); + } + + break; + } + + case GL_SAMPLER_1D: + { + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_1D_ARRAY, Locations, UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_1D, 0, UniformName, Locations)); + } + + break; + } + + case GL_SAMPLER_2D: + { + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_2D_ARRAY, Locations, UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_2D, 0, UniformName, Locations)); + } + + break; + } + + case GL_SAMPLER_3D: + { + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_3D_ARRAY, Locations, UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_3D, 0, UniformName, Locations)); + } + + break; + } + + case GL_SAMPLER_CUBE: + { + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_CUBE_ARRAY, Locations, UniformArrayName, Locations)); + } + else + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_CUBE, 0, UniformName, Locations)); + } + + break; + } + + case GL_IMAGE_1D: + { + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_IMAGE_1D_ARRAY, std::vector(UniformSize), UniformArrayName, Locations)); + } + else + { + // TODO: check if this make sense + GLint BindingPoint; + FE_GL_ERROR(glGetUniformiv(ProgramID, Locations[0], &BindingPoint)); + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_IMAGE_1D, BindingPoint, UniformName, Locations)); + } + + break; + } + + case GL_IMAGE_2D: + { + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_IMAGE_2D_ARRAY, std::vector(UniformSize), UniformArrayName, Locations)); + } + else + { + // TODO: check if this make sense + GLint BindingPoint; + FE_GL_ERROR(glGetUniformiv(ProgramID, Locations[0], &BindingPoint)); + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_IMAGE_2D, BindingPoint, UniformName, Locations)); + } + + break; + } + + case GL_IMAGE_3D: + { + if (bIsArray) + { + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_IMAGE_3D_ARRAY, std::vector(UniformSize), UniformArrayName, Locations)); + } + else + { + // TODO: check if this make sense + GLint BindingPoint; + FE_GL_ERROR(glGetUniformiv(ProgramID, Locations[0], &BindingPoint)); + AddUniformInternal(FEShaderUniform(FE_SHADER_UNIFORM_TYPE::FE_IMAGE_3D, BindingPoint, UniformName, Locations)); + } + break; } default: + { + LOG.Add("FEShader::RegisterUniforms() unknown uniform type: " + std::to_string(UniformType), "FE_LOG_RENDERING", FE_LOG_ERROR); break; + } } } @@ -472,12 +481,7 @@ void FEShader::RegisterUniforms() { for (size_t i = 0; i < FE_MAX_TEXTURES_PER_MATERIAL; i++) { - std::string Temp = "textures[" + std::to_string(i) + "]"; - std::string SecondTemp = "textureBindings[" + std::to_string(i) + "]"; - std::string ThirdTemp = "textureChannels[" + std::to_string(i) + "]"; - FE_GL_ERROR(glUniform1i(glGetUniformLocation(ProgramID, Temp.c_str()), static_cast(i))); - UniformLocations[static_cast(std::hash{}(SecondTemp))] = glGetUniformLocation(ProgramID, SecondTemp.c_str()); - UniformLocations[static_cast(std::hash{}(ThirdTemp))] = glGetUniformLocation(ProgramID, ThirdTemp.c_str()); + FE_GL_ERROR(glUniform1i(GetUniform("textures")->Locations[i], static_cast(i))); } // 16 textures for material + 4 CSM textures at the end. Next available binding is 20. Max is 27. @@ -514,6 +518,7 @@ void FEShader::RegisterUniforms() FE_GL_ERROR(glUniform1i(glGetUniformLocation(ProgramID, "CSM2"), FE_CSM_UNIT + 2)); FE_GL_ERROR(glUniform1i(glGetUniformLocation(ProgramID, "CSM3"), FE_CSM_UNIT + 3)); } + Stop(); } @@ -618,7 +623,7 @@ void FEShader::CleanUp() delete[] ComputeShaderText; ComputeShaderText = nullptr; - Parameters.clear(); + Uniforms.clear(); #ifdef FE_DEBUG_ENABLED if (bDebugRequest) FE_GL_ERROR(glDeleteBuffers(1, &SSBO)); @@ -890,7 +895,7 @@ std::string FEShader::ParseShaderForMacro(const char* ShaderText) ParsedShaderText.erase(Index, strlen(FE_DEBUG_MACRO)); } - // find next if it is exist + // Find next if it exists Index = ParsedShaderText.find(FE_DEBUG_MACRO); } @@ -937,150 +942,56 @@ std::string FEShader::ParseShaderForMacro(const char* ShaderText) return ParsedShaderText; } -GLuint FEShader::GetUniformLocation(const int& UniformNameHash) -{ - return UniformLocations[UniformNameHash]; -} - -void FEShader::LoadScalar(const int& UniformNameHash, const GLboolean& Value) -{ - FE_GL_ERROR(glUniform1f(UniformLocations[UniformNameHash], Value)); -} - -void FEShader::LoadScalar(const int& UniformNameHash, const GLfloat& Value) -{ - FE_GL_ERROR(glUniform1f(UniformLocations[UniformNameHash], Value)); -} - -void FEShader::LoadScalar(const int& UniformNameHash, const GLint& Value) -{ - FE_GL_ERROR(glUniform1i(UniformLocations[UniformNameHash], Value)); -} - -void FEShader::LoadVector(const int& UniformNameHash, const glm::vec2& Vector) +void FEShader::LoadUniformsDataToGPU() { - FE_GL_ERROR(glUniform2f(UniformLocations[UniformNameHash], Vector.x, Vector.y)); -} - -void FEShader::LoadVector(const int& UniformNameHash, const glm::vec3& Vector) -{ - FE_GL_ERROR(glUniform3f(UniformLocations[UniformNameHash], Vector.x, Vector.y, Vector.z)); -} - -void FEShader::LoadVector(const int& UniformNameHash, const glm::vec4& Vector) -{ - FE_GL_ERROR(glUniform4f(UniformLocations[UniformNameHash], Vector.x, Vector.y, Vector.z, Vector.w)); + auto UniformIterator = Uniforms.begin(); + while (UniformIterator != Uniforms.end()) + { + UniformIterator->second.LoadUniformToGPU(); + UniformIterator++; + } } -void FEShader::LoadMatrix(const int& UniformNameHash, glm::mat4& Matrix) +bool FEShader::LoadUniformDataToGPU(std::string UniformName) { - FE_GL_ERROR(glUniformMatrix4fv(UniformLocations[UniformNameHash], 1, false, glm::value_ptr(Matrix))); -} + FEShaderUniform* Uniform = GetUniform(UniformName); + if (Uniform == nullptr) + return false; -void FEShader::LoadIntArray(const int& UniformNameHash, const GLint* Array, const size_t ArraySize) -{ - FE_GL_ERROR(glUniform1iv(UniformLocations[UniformNameHash], static_cast(ArraySize), Array)); -} + Uniform->LoadUniformToGPU(); -void FEShader::LoadIntArray(const GLuint UniformLocation, const GLint* Array, const size_t ArraySize) -{ - FE_GL_ERROR(glUniform1iv(UniformLocation, static_cast(ArraySize), Array)); + return true; } -void FEShader::LoadFloatArray(const int& UniformNameHash, const GLfloat* Array, const size_t ArraySize) +void FEShader::AddUniformInternal(FEShaderUniform NewUniform) { - FE_GL_ERROR(glUniform1fv(UniformLocations[UniformNameHash], static_cast(ArraySize), Array)); + Uniforms[NewUniform.GetName()] = NewUniform; } -void FEShader::LoadDataToGPU() +std::vector FEShader::GetUniformNameList() { - auto iterator = Parameters.begin(); - while (iterator != Parameters.end()) - { - if (iterator->second.Data == nullptr) - continue; - - switch (iterator->second.Type) - { - case FE_BOOL_UNIFORM: - { - LoadScalar(iterator->second.NameHash, *static_cast(iterator->second.Data)); - break; - } - - case FE_INT_SCALAR_UNIFORM: - { - LoadScalar(iterator->second.NameHash, *static_cast(iterator->second.Data)); - break; - } - - case FE_FLOAT_SCALAR_UNIFORM: - { - LoadScalar(iterator->second.NameHash, *static_cast(iterator->second.Data)); - break; - } - - case FE_VECTOR2_UNIFORM: - { - LoadVector(iterator->second.NameHash, *static_cast(iterator->second.Data)); - break; - } - - case FE_VECTOR3_UNIFORM: - { - LoadVector(iterator->second.NameHash, *static_cast(iterator->second.Data)); - break; - } - - case FE_VECTOR4_UNIFORM: - { - LoadVector(iterator->second.NameHash, *static_cast(iterator->second.Data)); - break; - } - - case FE_MAT4_UNIFORM: - { - LoadMatrix(iterator->second.NameHash, *static_cast(iterator->second.Data)); - break; - } - - default: - break; - } - iterator++; - } + FE_MAP_TO_STR_VECTOR(Uniforms) } -void FEShader::AddParameter(FEShaderParam Parameter) +FEShaderUniform* FEShader::GetUniform(const std::string Name) { - /*bool find = false; - for (size_t i = 0; i < FEStandardUniforms.size(); i++) + if (Uniforms.find(Name) == Uniforms.end()) { - if (Parameter.getName().find(FEStandardUniforms[i]) != GL_INVALID_INDEX) - find = true; + LOG.Add(std::string("getParameter can't find : ") + Name + " in function FEShader::getParameter", "FE_LOG_RENDERING", FE_LOG_WARNING); + return nullptr; } - Parameter.loadedFromEngine = find;*/ - - Parameters[Parameter.GetName()] = Parameter; - Parameters[Parameter.GetName()].NameHash = static_cast(std::hash{}(Parameter.GetName())); - UniformLocations[Parameters[Parameter.GetName()].NameHash] = glGetUniformLocation(ProgramID, Parameter.GetName().c_str()); + return &Uniforms[Name]; } -std::vector FEShader::GetParameterList() +bool FEShader::GetUniformData(std::string UniformName, FEShaderUniformValue& ReturnedValue) { - FE_MAP_TO_STR_VECTOR(Parameters) -} - -FEShaderParam* FEShader::GetParameter(const std::string Name) -{ - if (Parameters.find(Name) == Parameters.end()) - { - LOG.Add(std::string("getParameter can't find : ") + Name + " in function FEShader::getParameter", "FE_LOG_RENDERING", FE_LOG_WARNING); - return nullptr; - } + FEShaderUniform* Uniform = GetUniform(UniformName); + if (Uniform == nullptr) + return false; - return &Parameters[Name]; + ReturnedValue = Uniform->CurrentValue; + return true; } std::vector FEShader::GetTextureList() @@ -1282,29 +1193,244 @@ void FEShader::ReCompile(const std::string Name, const char* VertexText, const c #ifdef FE_DEBUG_ENABLED CreateSSBO(); #endif + RegisterActiveAttributes(); RegisterUniforms(); } -void FEShader::AddParametersFromShader(FEShader* Shader) +void FEShader::AddUniformsFromShader(FEShader* Shader) { if (Shader == nullptr) return; - auto iterator = Parameters.begin(); - while (iterator != Parameters.end()) + auto UniformIterator = Shader->Uniforms.begin(); + while (UniformIterator != Shader->Uniforms.end()) { - AddParameter(iterator->second); - iterator++; + AddUniformInternal(UniformIterator->second); + UniformIterator++; } } -void* FEShader::GetParameterData(std::string Name) +void FEShader::RegisterActiveAttributes() { - if (Parameters.find(Name) == Parameters.end()) + GLint AttributeCount; + GLint AttributeSize; + GLenum AttributeType; + GLsizei ActualAttributeNameLength; + + // Get number of active attributes + FE_GL_ERROR(glGetProgramiv(ProgramID, GL_ACTIVE_ATTRIBUTES, &AttributeCount)); + + for (size_t i = 0; i < static_cast(AttributeCount); i++) { - LOG.Add(std::string("GetParameterData can't find : ") + Name + " in function FEShader::GetParameterData", "FE_LOG_RENDERING", FE_LOG_WARNING); - return nullptr; - } + const GLsizei MaxAttributeNameLength = 512; + GLchar AttributeName[MaxAttributeNameLength]; + + // Get attribute info + FE_GL_ERROR(glGetActiveAttrib(ProgramID, + static_cast(i), + MaxAttributeNameLength, + &ActualAttributeNameLength, + &AttributeSize, + &AttributeType, + AttributeName)); + + std::string AttributeNameString = AttributeName; + + // Check if it's an array attribute + bool bIsArray = ((AttributeSize > 1) && (AttributeNameString.find('[') != std::string::npos)); + std::string AttributeArrayName = ""; + + if (bIsArray) + AttributeArrayName = AttributeNameString.substr(0, AttributeNameString.find('[')); + + std::vector Locations; + if (bIsArray) + { + for (size_t i = 0; i < AttributeSize; i++) + { + std::string CurrentVariableName = AttributeArrayName + "[" + std::to_string(i) + "]"; + GLuint Location = -1; + FE_GL_ERROR(Location = glGetAttribLocation(ProgramID, CurrentVariableName.c_str())); + Locations.push_back(Location); + } + } + else + { + GLuint Location = -1; + FE_GL_ERROR(Location = glGetAttribLocation(ProgramID, AttributeName)); + Locations.push_back(Location); + } + + switch (AttributeType) + { + case GL_BOOL: + { + + + break; + } + + case GL_BOOL_VEC2: + { + + + break; + } + + case GL_BOOL_VEC3: + { + + + break; + } + + case GL_BOOL_VEC4: + { + + + break; + } + + case GL_UNSIGNED_INT: + { + + + break; + } + + case GL_INT: + { + int y = 0; + y++; + + break; + } + + case GL_INT_VEC2: + { + int y = 0; + y++; + + break; + } + + case GL_INT_VEC3: + { + int y = 0; + y++; + + break; + } + + case GL_INT_VEC4: + { + int y = 0; + y++; + + break; + } + + case GL_FLOAT: + { + int y = 0; + y++; + + break; + } + + case GL_FLOAT_VEC2: + { + int y = 0; + y++; + + break; + } + + case GL_FLOAT_VEC3: + { + int y = 0; + y++; + + break; + } + + case GL_FLOAT_VEC4: + { + int y = 0; + y++; + + break; + } + + case GL_FLOAT_MAT2: + { + int y = 0; + y++; + + break; + } + + case GL_FLOAT_MAT3: + { + int y = 0; + y++; + + break; + } + + case GL_FLOAT_MAT4: + { + int y = 0; + y++; + + break; + } + + case GL_DOUBLE: + { + + break; + } + + case GL_DOUBLE_VEC2: + { + + break; + } + + case GL_DOUBLE_VEC3: + { + + break; + } + + case GL_DOUBLE_VEC4: + { - return Parameters[Name].Data; + break; + } + + case GL_DOUBLE_MAT2: + { + + break; + } + + case GL_DOUBLE_MAT3: + { + + break; + } + + case GL_DOUBLE_MAT4: + { + + break; + } + + default: + break; + } + + } } \ No newline at end of file diff --git a/Renderer/FEShader.h b/Renderer/FEShader.h index 6199870..2ca04e5 100644 --- a/Renderer/FEShader.h +++ b/Renderer/FEShader.h @@ -1,6 +1,6 @@ #pragma once -#include "FETexture.h" +#include "FEShaderUniform.h" #define FE_VERTEX_ATTRIBUTE_POSITION "@In_Position@" #define FE_VERTEX_ATTRIBUTE_COLOR "@In_Color@" @@ -34,18 +34,6 @@ namespace FocalEngine { - enum FE_SHADER_PARAM_TYPE - { - FE_INT_SCALAR_UNIFORM = 0, - FE_FLOAT_SCALAR_UNIFORM = 1, - FE_VECTOR2_UNIFORM = 2, - FE_VECTOR3_UNIFORM = 3, - FE_VECTOR4_UNIFORM = 4, - FE_MAT4_UNIFORM = 5, - FE_NULL_UNIFORM = 6, - FE_BOOL_UNIFORM = 7 - }; - enum FE_VERTEX_ATTRIBUTES { FE_POSITION = 1 << 0, @@ -58,101 +46,34 @@ namespace FocalEngine FE_INSTANCEDATA = 1 << 7 }; - struct FEShaderParam - { - FEShaderParam(); - FEShaderParam(bool Data, std::string Name); - FEShaderParam(int Data, std::string Name); - FEShaderParam(float Data, std::string Name); - FEShaderParam(glm::vec2 Data, std::string Name); - FEShaderParam(glm::vec3 Data, std::string Name); - FEShaderParam(glm::vec4 Data, std::string Name); - FEShaderParam(glm::mat4 Data, std::string Name); - - void CopyCode(const FEShaderParam& Copy); - FEShaderParam(const FEShaderParam& Copy); - void operator=(const FEShaderParam& Assign); - - ~FEShaderParam(); - - int NameHash = 0; - std::string GetName(); - void SetName(std::string NewName); - - void* Data; - FE_SHADER_PARAM_TYPE Type; - std::string Name; - }; - - class FEMaterial; - class FERenderer; - class FEPostProcess; - class FEngine; - class FEResourceManager; - class FEShader : public FEObject { - friend FEMaterial; - friend FERenderer; - friend FEPostProcess; - friend FEngine; - friend FEResourceManager; + friend class FEMaterial; + friend class FERenderer; + friend class FEPostProcess; + friend class FEngine; + friend class FEResourceManager; public: FEShader(std::string Name, const char* VertexText, const char* FragmentText, - const char* TessControlText = nullptr, const char* TessEvalText = nullptr, - const char* GeometryText = nullptr, const char* ComputeText = nullptr, bool TestCompilation = false, int GlslVersion = 450); + const char* TessControlText = nullptr, const char* TessEvalText = nullptr, + const char* GeometryText = nullptr, const char* ComputeText = nullptr, bool TestCompilation = false, int GlslVersion = 450); ~FEShader(); FEShader(const FEShader& Shader); void operator= (const FEShader& Shader); - virtual void Start(); - virtual void Stop(); - - void LoadScalar(const int& UniformNameHash, const GLboolean& Value); - void LoadScalar(const int& UniformNameHash, const GLfloat& Value); - void LoadScalar(const int& UniformNameHash, const GLint& Value); - void LoadVector(const int& UniformNameHash, const glm::vec2& Vector); - void LoadVector(const int& UniformNameHash, const glm::vec3& Vector); - void LoadVector(const int& UniformNameHash, const glm::vec4& Vector); - void LoadMatrix(const int& UniformNameHash, glm::mat4& Matrix); - void LoadIntArray(const int& UniformNameHash, const GLint* Array, size_t ArraySize); - void LoadIntArray(GLuint UniformLocation, const GLint* Array, size_t ArraySize); - void LoadFloatArray(const int& UniformNameHash, const GLfloat* Array, size_t ArraySize); - - virtual void LoadDataToGPU(); - virtual void AddParameter(FEShaderParam Parameter); - - std::vector GetParameterList(); - void* GetParameterData(std::string Name); + void Start(); + void Stop(); + + void LoadUniformsDataToGPU(); + bool LoadUniformDataToGPU(std::string UniformName); + + std::vector GetUniformNameList(); std::vector GetTextureList(); + bool GetUniformData(std::string UniformName, FEShaderUniformValue& ReturnedValue); template - void UpdateParameterData(std::string Name, const T& Data); - - template<> - void UpdateParameterData(std::string Name, void* const& Data); - - template<> - void UpdateParameterData(std::string Name, const bool& Data); - - template<> - void UpdateParameterData(std::string Name, const int& Data); - - template<> - void UpdateParameterData(std::string Name, const float& Data); - - template<> - void UpdateParameterData(std::string Name, const glm::vec2& Data); - - template<> - void UpdateParameterData(std::string Name, const glm::vec3& Data); - - template<> - void UpdateParameterData(std::string Name, const glm::vec4& Data); - - template<> - void UpdateParameterData(std::string Name, const glm::mat4& Data); + bool UpdateUniformData(std::string UniformName, const T& Data); char* GetVertexShaderText(); char* GetTessControlShaderText(); @@ -171,7 +92,8 @@ namespace FocalEngine void Dispatch(GLuint GroupXCount, GLuint GroupYCount, GLuint GroupZCount); - void AddParametersFromShader(FEShader* Shader); + // TODO: It should not be public. + void AddUniformsFromShader(FEShader* Shader); private: void CopyCode(const FEShader& Shader); @@ -195,19 +117,21 @@ namespace FocalEngine int VertexAttributes = 0; - std::unordered_map Parameters; - FEShaderParam* GetParameter(std::string Name); + std::unordered_map Uniforms; + FEShaderUniform* GetUniform(std::string Name); + std::unordered_map BlockUniforms; GLuint LoadShader(const char* ShaderText, GLuint ShaderType); bool LinkProgram(); void CleanUp(); void BindAttributes(); - std::unordered_map UniformLocations; - GLuint GetUniformLocation(const int& UniformNameHash); + + void AddUniformInternal(FEShaderUniform NewUniform); std::vector TextureUniforms; std::string ParseShaderForMacro(const char* ShaderText); + void RegisterActiveAttributes(); void RegisterUniforms(); bool bCSM = false; diff --git a/Renderer/FEShader.inl b/Renderer/FEShader.inl index 4794c1a..0ad3771 100644 --- a/Renderer/FEShader.inl +++ b/Renderer/FEShader.inl @@ -1,200 +1,14 @@ #pragma once template -void FEShader::UpdateParameterData(std::string Name, const T& Data) +bool FEShader::UpdateUniformData(std::string UniformName, const T& Data) { - LOG.Add("FEShader::UpdateParameterData() unknown type", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; -} - -template<> -void FEShader::UpdateParameterData(std::string Name, void* const& Data) -{ - FEShaderParam* Parameter = GetParameter(Name); - if (Parameter == nullptr) - { - LOG.Add("FEShader::UpdateParameterData() parameter not found", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - switch (Type) - { - case FE_BOOL_UNIFORM: - { - *static_cast(Parameter->Data) = *static_cast(Data); - break; - } - - case FE_INT_SCALAR_UNIFORM: - { - *static_cast(Parameter->Data) = *static_cast(Data); - break; - } - - case FE_FLOAT_SCALAR_UNIFORM: - { - *static_cast(Parameter->Data) = *static_cast(Data); - break; - } - - case FE_VECTOR2_UNIFORM: - { - *static_cast(Parameter->Data) = *static_cast(Data); - break; - } - - case FE_VECTOR3_UNIFORM: - { - *static_cast(Parameter->Data) = *static_cast(Data); - break; - } - - case FE_VECTOR4_UNIFORM: - { - *static_cast(Parameter->Data) = *static_cast(Data); - break; - } - - case FE_MAT4_UNIFORM: - { - *static_cast(Parameter->Data) = *static_cast(Data); - break; - } - - default: - break; - } -} - -template<> -void FEShader::UpdateParameterData(std::string Name, const bool& Data) -{ - FEShaderParam* Parameter = GetParameter(Name); - if (Parameter == nullptr) - { - LOG.Add("FEShader::UpdateParameterData() parameter not found", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - if (Parameter->Type != FE_BOOL_UNIFORM) - { - LOG.Add("FEShader::UpdateParameterData() incorrect type", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - *static_cast(Parameter->Data) = Data; -} - -template<> -void FEShader::UpdateParameterData(std::string Name, const int& Data) -{ - FEShaderParam* Parameter = GetParameter(Name); - if (Parameter == nullptr) - { - LOG.Add("FEShader::UpdateParameterData() parameter not found", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - if (Parameter->Type != FE_INT_SCALAR_UNIFORM) - { - LOG.Add("FEShader::UpdateParameterData() incorrect type", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - *static_cast(Parameter->Data) = Data; -} - -template<> -void FEShader::UpdateParameterData(std::string Name, const float& Data) -{ - FEShaderParam* Parameter = GetParameter(Name); - if (Parameter == nullptr) - { - LOG.Add("FEShader::UpdateParameterData() parameter not found", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - if (Parameter->Type != FE_FLOAT_SCALAR_UNIFORM) - { - LOG.Add("FEShader::UpdateParameterData() incorrect type", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - *static_cast(Parameter->Data) = Data; -} - -template<> -void FEShader::UpdateParameterData(std::string Name, const glm::vec2& Data) -{ - FEShaderParam* Parameter = GetParameter(Name); - if (Parameter == nullptr) - { - LOG.Add("FEShader::UpdateParameterData() parameter not found", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - if (Parameter->Type != FE_VECTOR2_UNIFORM) - { - LOG.Add("FEShader::UpdateParameterData() incorrect type", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - *static_cast(Parameter->Data) = Data; -} - -template<> -void FEShader::UpdateParameterData(std::string Name, const glm::vec3& Data) -{ - FEShaderParam* Parameter = GetParameter(Name); - if (Parameter == nullptr) - { - LOG.Add("FEShader::UpdateParameterData() parameter not found", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - if (Parameter->Type != FE_VECTOR3_UNIFORM) - { - LOG.Add("FEShader::UpdateParameterData() incorrect type", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - *static_cast(Parameter->Data) = Data; -} - -template<> -void FEShader::UpdateParameterData(std::string Name, const glm::vec4& Data) -{ - FEShaderParam* Parameter = GetParameter(Name); - if (Parameter == nullptr) - { - LOG.Add("FEShader::UpdateParameterData() parameter not found", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - if (Parameter->Type != FE_VECTOR4_UNIFORM) - { - LOG.Add("FEShader::UpdateParameterData() incorrect type", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - *static_cast(Parameter->Data) = Data; -} - -template<> -void FEShader::UpdateParameterData(std::string Name, const glm::mat4& Data) -{ - FEShaderParam* Parameter = GetParameter(Name); - if (Parameter == nullptr) - { - LOG.Add("FEShader::UpdateParameterData() parameter not found", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - if (Parameter->Type != FE_MAT4_UNIFORM) + FEShaderUniform* Uniform = GetUniform(UniformName); + if (Uniform == nullptr) { - LOG.Add("FEShader::UpdateParameterData() incorrect type", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; + LOG.Add("FEShader::UpdateUniformData() uniform with name: " + UniformName + " not found!", "FE_LOG_RENDERING", FE_LOG_ERROR); + return false; } - *static_cast(Parameter->Data) = Data; + return Uniform->SetValue(Data); } \ No newline at end of file diff --git a/Renderer/FEShaderUniform.cpp b/Renderer/FEShaderUniform.cpp new file mode 100644 index 0000000..44a0256 --- /dev/null +++ b/Renderer/FEShaderUniform.cpp @@ -0,0 +1,170 @@ +#include "FEShaderUniform.h" +using namespace FocalEngine; + +FEShaderUniformValue::FEShaderUniformValue() +{ +} + +std::string FEShaderUniformValue::GetName() const +{ + return Name; +} + +void FEShaderUniformValue::SetName(const std::string NewName) +{ + Name = NewName; +} + +std::vector FEShaderUniformValue::GetCompatibleTypes() const +{ + return CompatibleTypes; +} + +FEShaderUniform::FEShaderUniform() +{ +} + +std::string FEShaderUniform::GetName() +{ + return Name; +} + +void FEShaderUniform::SetName(const std::string NewName) +{ + Name = NewName; +} + +FEShaderUniform::~FEShaderUniform() +{ +} + +void FEShaderUniform::LoadUniformToGPU() +{ + switch (Type) + { + case FE_SHADER_UNIFORM_TYPE::FE_BOOL: + { + FE_GL_ERROR(glUniform1f(Locations[0], static_cast(GetValue()))); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_BOOL_ARRAY: + { + const auto& Array = GetValue>(); + std::vector FloatArray(Array.size()); + for (size_t i = 0; i < Array.size(); i++) + { + FloatArray[i] = static_cast(Array[i]); + } + FE_GL_ERROR(glUniform1fv(Locations[0], static_cast(Array.size()), FloatArray.data())); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_UNSIGNED_INT: + { + FE_GL_ERROR(glUniform1ui(Locations[0], GetValue())); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_UNSIGNED_INT_ARRAY: + { + const auto& Array = GetValue>(); + FE_GL_ERROR(glUniform1uiv(Locations[0], static_cast(Array.size()), Array.data())); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_INT: + { + FE_GL_ERROR(glUniform1i(Locations[0], GetValue())); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_INT_ARRAY: + { + const auto& Array = GetValue>(); + // TODO: in 64-bit systems Array.size() will be 64 bit, but GL expects 32 bit, it could cause problems + FE_GL_ERROR(glUniform1iv(Locations[0], static_cast(Array.size()), Array.data())); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_FLOAT: + { + FE_GL_ERROR(glUniform1f(Locations[0], GetValue())); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_FLOAT_ARRAY: + { + const auto& Array = GetValue>(); + FE_GL_ERROR(glUniform1fv(Locations[0], static_cast(Array.size()), Array.data())); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR2: + { + const auto& value = GetValue(); + FE_GL_ERROR(glUniform2f(Locations[0], value.x, value.y)); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR2_ARRAY: + { + const auto& Array = GetValue>(); + FE_GL_ERROR(glUniform2fv(Locations[0], static_cast(Array.size()), glm::value_ptr(Array[0]))); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR3: + { + const auto& Value = GetValue(); + FE_GL_ERROR(glUniform3f(Locations[0], Value.x, Value.y, Value.z)); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR3_ARRAY: + { + const auto& Array = GetValue>(); + FE_GL_ERROR(glUniform3fv(Locations[0], static_cast(Array.size()), glm::value_ptr(Array[0]))); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR4: + { + const auto& Value = GetValue(); + FE_GL_ERROR(glUniform4f(Locations[0], Value.x, Value.y, Value.z, Value.w)); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR4_ARRAY: + { + const auto& Array = GetValue>(); + FE_GL_ERROR(glUniform4fv(Locations[0], static_cast(Array.size()), glm::value_ptr(Array[0]))); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_MAT4: + { + const glm::mat4& Matrix = GetValue(); + FE_GL_ERROR(glUniformMatrix4fv(Locations[0], 1, false, glm::value_ptr(Matrix))); + break; + } + + case FE_SHADER_UNIFORM_TYPE::FE_MAT4_ARRAY: + { + const auto& Array = GetValue>(); + FE_GL_ERROR(glUniformMatrix4fv(Locations[0], static_cast(Array.size()), false, glm::value_ptr(Array[0]))); + break; + } + + default: + { + LOG.Add("FEShaderUniform::LoadUniformToGPU: Unknown uniform type for uniform: " + Name, "FE_LOG_RENDERING", FE_LOG_ERROR); + break; + } + } +} + +FE_SHADER_UNIFORM_TYPE FEShaderUniform::GetType() const +{ + return Type; +} \ No newline at end of file diff --git a/Renderer/FEShaderUniform.h b/Renderer/FEShaderUniform.h new file mode 100644 index 0000000..3fcc512 --- /dev/null +++ b/Renderer/FEShaderUniform.h @@ -0,0 +1,188 @@ +#pragma once + +#include "FETexture.h" +#include +#include + +namespace FocalEngine +{ + enum class FE_SHADER_UNIFORM_TYPE + { + FE_NULL = 0, + FE_BOOL = 1, + FE_BOOL_ARRAY = 2, + FE_UNSIGNED_INT = 3, + FE_UNSIGNED_INT_ARRAY = 4, + FE_INT = 5, + FE_INT_ARRAY = 6, + FE_FLOAT = 7, + FE_FLOAT_ARRAY = 8, + FE_FLOAT_VECTOR2 = 9, + FE_FLOAT_VECTOR2_ARRAY = 10, + FE_FLOAT_VECTOR3 = 11, + FE_FLOAT_VECTOR3_ARRAY = 12, + FE_FLOAT_VECTOR4 = 13, + FE_FLOAT_VECTOR4_ARRAY = 14, + FE_MAT4 = 15, + FE_MAT4_ARRAY = 16, + FE_SAMPLER_1D = 17, + FE_SAMPLER_1D_ARRAY = 18, + FE_SAMPLER_2D = 19, + FE_SAMPLER_2D_ARRAY = 20, + FE_SAMPLER_3D = 21, + FE_SAMPLER_3D_ARRAY = 22, + FE_SAMPLER_CUBE = 23, + FE_SAMPLER_CUBE_ARRAY = 24, + FE_IMAGE_1D = 25, + FE_IMAGE_1D_ARRAY = 26, + FE_IMAGE_2D = 27, + FE_IMAGE_2D_ARRAY = 28, + FE_IMAGE_3D = 29, + FE_IMAGE_3D_ARRAY = 30 + }; + + struct FEShaderUniformValue + { + friend struct FEShaderUniform; + friend class FEShader; + friend class FERenderer; + + FEShaderUniformValue(); + + template + FEShaderUniformValue(const std::string& Name, const T& Data); + + template + const T& GetValue() const; + + template + bool SetValue(const T& Value); + + std::vector GetCompatibleTypes() const; + template + bool IsType() const; + + std::string GetName() const; + void SetName(std::string NewName); + private: + std::any Data; + std::vector CompatibleTypes = { FE_SHADER_UNIFORM_TYPE::FE_NULL }; + std::type_index TypeIndex{ typeid(void) }; + std::string Name = ""; + + static const std::unordered_map& GetUniformTypeToCTypeMap() + { + static const std::unordered_map Mapping = { + {FE_SHADER_UNIFORM_TYPE::FE_BOOL, std::type_index(typeid(bool))}, + {FE_SHADER_UNIFORM_TYPE::FE_BOOL_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_UNSIGNED_INT, std::type_index(typeid(unsigned int))}, + {FE_SHADER_UNIFORM_TYPE::FE_UNSIGNED_INT_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_INT, std::type_index(typeid(int))}, + {FE_SHADER_UNIFORM_TYPE::FE_INT_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_FLOAT, std::type_index(typeid(float))}, + {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR2, std::type_index(typeid(glm::vec2))}, + {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR2_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR3, std::type_index(typeid(glm::vec3))}, + {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR3_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR4, std::type_index(typeid(glm::vec4))}, + {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR4_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_MAT4, std::type_index(typeid(glm::mat4))}, + {FE_SHADER_UNIFORM_TYPE::FE_MAT4_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_1D, std::type_index(typeid(unsigned int))}, + {FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_1D_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_2D, std::type_index(typeid(unsigned int))}, + {FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_2D_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_3D, std::type_index(typeid(unsigned int))}, + {FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_3D_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_CUBE, std::type_index(typeid(unsigned int))}, + {FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_CUBE_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_IMAGE_1D, std::type_index(typeid(unsigned int))}, + {FE_SHADER_UNIFORM_TYPE::FE_IMAGE_1D_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_IMAGE_2D, std::type_index(typeid(unsigned int))}, + {FE_SHADER_UNIFORM_TYPE::FE_IMAGE_2D_ARRAY, std::type_index(typeid(std::vector))}, + {FE_SHADER_UNIFORM_TYPE::FE_IMAGE_3D, std::type_index(typeid(unsigned int))}, + {FE_SHADER_UNIFORM_TYPE::FE_IMAGE_3D_ARRAY, std::type_index(typeid(std::vector))} + }; + + return Mapping; + } + + static const std::unordered_map>& GetCTypeToUniformTypesMap() + { + static const std::unordered_map> TypeToEnum = { + {std::type_index(typeid(bool)), {FE_SHADER_UNIFORM_TYPE::FE_BOOL}}, + {std::type_index(typeid(std::vector)), {FE_SHADER_UNIFORM_TYPE::FE_BOOL_ARRAY}}, + {std::type_index(typeid(unsigned int)), {FE_SHADER_UNIFORM_TYPE::FE_UNSIGNED_INT, + FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_1D, + FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_2D, + FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_3D, + FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_CUBE, + FE_SHADER_UNIFORM_TYPE::FE_IMAGE_1D, + FE_SHADER_UNIFORM_TYPE::FE_IMAGE_2D, + FE_SHADER_UNIFORM_TYPE::FE_IMAGE_3D}}, + {std::type_index(typeid(std::vector)), {FE_SHADER_UNIFORM_TYPE::FE_UNSIGNED_INT_ARRAY, + FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_1D_ARRAY, + FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_2D_ARRAY, + FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_3D_ARRAY, + FE_SHADER_UNIFORM_TYPE::FE_SAMPLER_CUBE_ARRAY, + FE_SHADER_UNIFORM_TYPE::FE_IMAGE_1D_ARRAY, + FE_SHADER_UNIFORM_TYPE::FE_IMAGE_2D_ARRAY, + FE_SHADER_UNIFORM_TYPE::FE_IMAGE_3D_ARRAY}}, + {std::type_index(typeid(int)), {FE_SHADER_UNIFORM_TYPE::FE_INT}}, + {std::type_index(typeid(std::vector)), {FE_SHADER_UNIFORM_TYPE::FE_INT_ARRAY}}, + {std::type_index(typeid(float)), {FE_SHADER_UNIFORM_TYPE::FE_FLOAT}}, + {std::type_index(typeid(std::vector)), {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_ARRAY}}, + {std::type_index(typeid(glm::vec2)), {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR2}}, + {std::type_index(typeid(std::vector)), {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR2_ARRAY}}, + {std::type_index(typeid(glm::vec3)), {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR3}}, + {std::type_index(typeid(std::vector)), {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR3_ARRAY}}, + {std::type_index(typeid(glm::vec4)), {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR4}}, + {std::type_index(typeid(std::vector)), {FE_SHADER_UNIFORM_TYPE::FE_FLOAT_VECTOR4_ARRAY}}, + {std::type_index(typeid(glm::mat4)), {FE_SHADER_UNIFORM_TYPE::FE_MAT4}}, + {std::type_index(typeid(std::vector)), {FE_SHADER_UNIFORM_TYPE::FE_MAT4_ARRAY}} + }; + + return TypeToEnum; + } + }; + + struct FEShaderUniform + { + friend class FEShader; + friend class FEMaterial; + friend class FERenderer; + + FEShaderUniform(); + ~FEShaderUniform(); + + template + FEShaderUniform(FE_SHADER_UNIFORM_TYPE Type, const T& DataToInitialize, const std::string& Name, std::vector& Locations); + + template + const T& GetValue() const; + + template + bool SetValue(const T& Value); + + FE_SHADER_UNIFORM_TYPE GetType() const; + template + bool IsType() const; + + std::string GetName(); + void SetName(std::string NewName); + private: + FEShaderUniformValue CurrentValue; + + FE_SHADER_UNIFORM_TYPE Type = FE_SHADER_UNIFORM_TYPE::FE_NULL; + std::type_index TypeIndex{ typeid(void) }; + std::string Name = ""; + + size_t ElementCount = 0; + std::vector Locations; + public: + void LoadUniformToGPU(); + }; + +#include "FEShaderUniform.inl" +} \ No newline at end of file diff --git a/Renderer/FEShaderUniform.inl b/Renderer/FEShaderUniform.inl new file mode 100644 index 0000000..9c13ebb --- /dev/null +++ b/Renderer/FEShaderUniform.inl @@ -0,0 +1,108 @@ +#pragma once + +template +FEShaderUniformValue::FEShaderUniformValue(const std::string& Name, const T& Data) +{ + this->Data = Data; + this->Name = Name; + TypeIndex = std::type_index(typeid(T)); + + auto& TypeMap = GetCTypeToUniformTypesMap(); + auto TypeMapIterator = TypeMap.find(TypeIndex); + if (TypeMapIterator != TypeMap.end()) + { + CompatibleTypes = TypeMapIterator->second; + } + else + { + throw std::runtime_error("Unsupported uniform type"); + } +} + +template +const T& FEShaderUniformValue::GetValue() const +{ + return std::any_cast(Data); +} + +template +bool FEShaderUniformValue::SetValue(const T& Value) +{ + if (TypeIndex != std::type_index(typeid(T))) + { + LOG.Add("FEShaderUniformValue::SetValue() failed, type mismatch", "FE_LOG_RENDERING", FE_LOG_ERROR); + return false; + } + + Data = Value; + return true; +} + +template +bool FEShaderUniformValue::IsType() const +{ + return TypeIndex == std::type_index(typeid(T)); +} + +template +FEShaderUniform::FEShaderUniform(FE_SHADER_UNIFORM_TYPE Type, const T& DataToInitialize, const std::string& Name, std::vector& Locations) +{ + if (Locations.empty()) + { + LOG.Add("FEShaderUniform::FEShaderUniform() failed, Locations vector is empty!", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } + + if (Name.empty()) + { + LOG.Add("FEShaderUniform::FEShaderUniform() failed, uniform name is empty!", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } + + auto& TypeMap = FEShaderUniformValue::GetCTypeToUniformTypesMap(); + auto TypeMapIterator = TypeMap.find(std::type_index(typeid(T))); + if (TypeMapIterator == TypeMap.end()) + { + LOG.Add("FEShaderUniform::FEShaderUniform() failed, unsupported uniform type!", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } + + TypeIndex = std::type_index(typeid(T)); + this->Type = Type; + + CurrentValue = FEShaderUniformValue(Name, DataToInitialize); + this->Name = Name; + this->Locations = Locations; +} + +template +const T& FEShaderUniform::GetValue() const +{ + if (!CurrentValue.IsType()) + { + //LOG.Add("FEShaderUniform::GetValue() failed, type mismatch", "FE_LOG_RENDERING", FE_LOG_ERROR); + //return T(); + // FIXME: Should return nullptr or similar instead of throwing an exception. + throw std::runtime_error("Type mismatch"); + } + + return CurrentValue.GetValue(); +} + +template +bool FEShaderUniform::SetValue(const T& Value) +{ + if (TypeIndex != std::type_index(typeid(T))) + { + LOG.Add("FEShaderUniform::SetValue() failed, type mismatch", "FE_LOG_RENDERING", FE_LOG_ERROR); + return false; + } + + return CurrentValue.SetValue(Value); +} + +template +bool FEShaderUniform::IsType() const +{ + return TypeIndex == std::type_index(typeid(T)); +} \ No newline at end of file diff --git a/Renderer/FETerrain.cpp b/Renderer/FETerrain.cpp deleted file mode 100644 index 5b15eee..0000000 --- a/Renderer/FETerrain.cpp +++ /dev/null @@ -1,876 +0,0 @@ -#include "FETerrain.h" -using namespace FocalEngine; - -FETerrainLayer::FETerrainLayer(const std::string Name) : FEObject(FE_TERRAIN_LAYER, Name) {} -FEMaterial* FETerrainLayer::GetMaterial() -{ - return Material; -} - -void FETerrainLayer::SetMaterial(FEMaterial* NewValue) -{ - if (NewValue->IsCompackPacking()) - Material = NewValue; -} - -FETerrain::FETerrain(const std::string Name) : FEObject(FE_TERRAIN, Name) -{ - SetName(Name); - NameHash = static_cast(std::hash{}(Name)); - - Layers.resize(FE_TERRAIN_MAX_LAYERS); - for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) - { - Layers[i] = nullptr; - } - - LayerMaps.resize(FE_TERRAIN_MAX_LAYERS / FE_TERRAIN_LAYER_PER_TEXTURE); - LayerMapsRawData.resize(FE_TERRAIN_MAX_LAYERS / FE_TERRAIN_LAYER_PER_TEXTURE); - LayerMaps[0] = nullptr; - LayerMaps[1] = nullptr; - - FE_GL_ERROR(glGenBuffers(1, &GPULayersDataBuffer)); - FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, GPULayersDataBuffer)); - FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(float) * FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * FE_TERRAIN_MAX_LAYERS, nullptr, GL_DYNAMIC_DRAW)); - - GPULayersData.resize(FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * FE_TERRAIN_MAX_LAYERS); - OldGPULayersData.resize(FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * FE_TERRAIN_MAX_LAYERS); -} - -FETerrain::~FETerrain() -{ - for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) - { - delete Layers[i]; - } - - FE_GL_ERROR(glDeleteBuffers(1, &GPULayersDataBuffer)); -} - -void FETerrain::Render() -{ - FE_GL_ERROR(glDrawArraysInstanced(GL_PATCHES, 0, 4, 64 * 64)); -} - -bool FETerrain::IsVisible() -{ - return bVisible; -} - -void FETerrain::SetVisibility(const bool NewValue) -{ - bVisible = NewValue; -} - -FEAABB FETerrain::GetAABB() -{ - if (Transform.bDirtyFlag) - { - Transform.bDirtyFlag = false; - //#fix it should be optimized. - FEAABB MeshAABB = AABB; - // -0.5f it is a little hack, because this -0.5f should be made during tessellation. - MeshAABB.Min = glm::vec3(-32.0f - 0.5f, AABB.GetMin()[1], -32.0f - 0.5f); - MeshAABB.Max = glm::vec3(32.0f + 64.0f * (ChunkPerSide - 1) - 0.5f, AABB.GetMax()[1], 32.0f + 64.0f * (ChunkPerSide - 1) - 0.5f); - MeshAABB = FEAABB(glm::vec3(MeshAABB.GetMin()[0], MeshAABB.GetMin()[1] * 2 * HightScale - HightScale, MeshAABB.GetMin()[2]), glm::vec3(MeshAABB.GetMax()[0], MeshAABB.GetMax()[1] * 2 * HightScale - HightScale, MeshAABB.GetMax()[2])); - - FinalAABB = MeshAABB.Transform(Transform.GetTransformMatrix()); - - XSize = FinalAABB.GetMax()[0] - FinalAABB.GetMin()[0]; - ZSize = FinalAABB.GetMax()[2] - FinalAABB.GetMin()[2]; - } - - return FinalAABB; -} - -FEAABB FETerrain::GetPureAABB() -{ - FEAABB MeshAABB = AABB; - MeshAABB.Min = glm::vec3(-32.0f, AABB.GetMin()[1], -32.0f); - MeshAABB.Max = glm::vec3(32.0f + 64.0f * (ChunkPerSide - 1), AABB.GetMax()[1], 32.0f + 64.0f * (ChunkPerSide - 1)); - MeshAABB = FEAABB(glm::vec3(MeshAABB.GetMin()[0], MeshAABB.GetMin()[1] * 2 * HightScale - HightScale, MeshAABB.GetMin()[2]), glm::vec3(MeshAABB.GetMax()[0], MeshAABB.GetMax()[1] * 2 * HightScale - HightScale, MeshAABB.GetMax()[2])); - return MeshAABB; -} - -bool FETerrain::IsCastingShadows() -{ - return bCastShadows; -} - -void FETerrain::SetCastingShadows(const bool NewValue) -{ - bCastShadows = NewValue; -} - -bool FETerrain::IsReceivingShadows() -{ - return bReceiveShadows; -} - -void FETerrain::SetReceivingShadows(const bool NewValue) -{ - bReceiveShadows = NewValue; -} - -void FETerrain::SetWireframeMode(const bool NewValue) -{ - bWireframeMode = NewValue; -} - -bool FETerrain::IsWireframeMode() -{ - return bWireframeMode; -} - -float FETerrain::GetHightScale() -{ - return HightScale; -} - -void FETerrain::SetHightScale(const float NewValue) -{ - if (NewValue <= 0) - return; - - if (HightScale != NewValue) - Transform.bDirtyFlag = true; - HightScale = NewValue; -} - -glm::vec2 FETerrain::GetTileMult() -{ - return TileMult; -} - -void FETerrain::SetTileMult(const glm::vec2 NewValue) -{ - TileMult = NewValue; -} - -float FETerrain::GetLODLevel() -{ - return LODLevel; -} - -void FETerrain::SetLODLevel(float NewValue) -{ - if (NewValue < 2.0) - NewValue = 2.0; - - if (NewValue > 128.0) - NewValue = 128.0; - - LODLevel = NewValue; -} - -float FETerrain::GetChunkPerSide() -{ - return ChunkPerSide; -} - -void FETerrain::SetChunkPerSide(float NewValue) -{ - if (NewValue < 1.0f) - NewValue = 1.0f; - - if (NewValue > 16.0f) - NewValue = 16.0f; - - if (ChunkPerSide != NewValue) - Transform.bDirtyFlag = true; - ChunkPerSide = NewValue; -} - -float FETerrain::GetDisplacementScale() -{ - return DisplacementScale; -} - -void FETerrain::SetDisplacementScale(const float NewValue) -{ - DisplacementScale = NewValue; -} - -float FETerrain::GetHeightAt(glm::vec2 XZWorldPosition) -{ - float LocalX = XZWorldPosition[0]; - float LocalZ = XZWorldPosition[1]; - - LocalX -= FinalAABB.GetMin()[0]; - LocalZ -= FinalAABB.GetMin()[2]; - - if (XSize == 0 || ZSize == 0) - GetAABB(); - - LocalX = LocalX / XSize; - LocalZ = LocalZ / ZSize; - - if (LocalX > 0 && LocalZ > 0 && LocalX < 1.0 && LocalZ < 1.0) - { - LocalX = static_cast(static_cast(LocalX * this->HeightMap->GetWidth())); - LocalZ = static_cast(static_cast(LocalZ * this->HeightMap->GetHeight())); - - const int Index = static_cast(LocalZ * this->HeightMap->GetWidth() + LocalX); - return (HeightMapArray[Index] * 2 * HightScale - HightScale) * Transform.GetScale()[1] + Transform.GetPosition()[1]; - } - - return -FLT_MAX; -} - -float FETerrain::GetXSize() -{ - return XSize; -} - -float FETerrain::GetZSize() -{ - return ZSize; -} - -// ********************************** PointOnTerrain ********************************** -bool FETerrain::IsUnderGround(const glm::dvec3 TestPoint) -{ - const float Height = GetHeightAt(glm::vec2(TestPoint.x, TestPoint.z)); - // if we go outside terrain. - if (Height == -FLT_MAX) - return true; - - return TestPoint.y < Height ? true : false; -} - -glm::dvec3 FETerrain::GetPointOnRay(const glm::dvec3 MouseRayStart, const glm::dvec3 MouseRayDirection, const float Distance) -{ - const glm::dvec3 Start = glm::dvec3(MouseRayStart.x, MouseRayStart.y, MouseRayStart.z); - const glm::dvec3 ScaledRay = glm::dvec3(MouseRayDirection.x * Distance, MouseRayDirection.y * Distance, MouseRayDirection.z * Distance); - - return Start + ScaledRay; -} - -bool FETerrain::IntersectionInRange(const float Start, const float Finish, const glm::dvec3 MouseRayStart, const glm::dvec3 MouseRayDirection) -{ - const glm::dvec3 StartPoint = GetPointOnRay(MouseRayStart, MouseRayDirection, Start); - const glm::dvec3 EndPoint = GetPointOnRay(MouseRayStart, MouseRayDirection, Finish); - return !IsUnderGround(StartPoint) && IsUnderGround(EndPoint) ? true : false; -} - -glm::dvec3 FETerrain::BinarySearch(const int Count, const float Start, const float Finish, const glm::dvec3 MouseRayStart, const glm::dvec3 MouseRayDirection) -{ - const int RecursionCount = 200; - const float Half = Start + ((Finish - Start) / 2.0f); - if (Count >= RecursionCount) - { - const glm::dvec3 EndPoint = GetPointOnRay(MouseRayStart, MouseRayDirection, Half); - if (GetHeightAt(glm::vec2(EndPoint.x, EndPoint.z)) != -1.0f) - { - return EndPoint; - } - else - { - return glm::dvec3(FLT_MAX); - } - } - - if (IntersectionInRange(Start, Half, MouseRayStart, MouseRayDirection)) - { - return BinarySearch(Count + 1, Start, Half, MouseRayStart, MouseRayDirection); - } - else - { - return BinarySearch(Count + 1, Half, Finish, MouseRayStart, MouseRayDirection); - } -} - -glm::dvec3 FETerrain::GetPointOnTerrain(const glm::dvec3 MouseRayStart, const glm::dvec3 MouseRayDirection, const float StartDistance, const float EndDistance) -{ - if (IntersectionInRange(StartDistance, EndDistance, MouseRayStart, MouseRayDirection)) - { - const glm::dvec3 PointOnTerrain = BinarySearch(0, StartDistance, EndDistance, MouseRayStart, MouseRayDirection); - // if point is not above terrain, point could be above because isUnderGround returning true if we go out of terrain bounds to fix some bugs. - if ((GetHeightAt(glm::vec2(PointOnTerrain.x, PointOnTerrain.z)) + 1.0f) > PointOnTerrain.y) - return PointOnTerrain; - - return glm::dvec3(FLT_MAX); - } - - return glm::dvec3(FLT_MAX); -} - -void FETerrain::UpdateCpuHeightInfo() -{ - /*FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, heightMap->getTextureID())); - - size_t rawDataLenght = heightMap->getWidth() * heightMap->getHeight() * 2; - unsigned char* rawData = new unsigned char[rawDataLenght]; - glPixelStorei(GL_PACK_ALIGNMENT, 2); - FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_SHORT, rawData)); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - heightMap->unBind();*/ - - size_t RawDataLenght; - unsigned char* RawData = HeightMap->GetRawData(&RawDataLenght); - - float Max = FLT_MIN; - float Min = FLT_MAX; - int Iterator = 0; - for (size_t i = 0; i < RawDataLenght; i += 2) - { - const unsigned short Temp = *(unsigned short*)(&RawData[i]); - HeightMapArray[Iterator] = Temp / static_cast(0xFFFF); - - if (Max < HeightMapArray[Iterator]) - Max = HeightMapArray[Iterator]; - - if (Min > HeightMapArray[Iterator]) - Min = HeightMapArray[Iterator]; - - Iterator++; - } - - const glm::vec3 MinPoint = glm::vec3(-1.0f, Min, -1.0f); - const glm::vec3 MaxPoint = glm::vec3(1.0f, Max, 1.0f); - AABB = FEAABB(MinPoint, MaxPoint); - Transform.bDirtyFlag = true; - - delete[] RawData; -} -// ********************************** PointOnTerrain END ********************************** - -// **************************** TERRAIN EDITOR TOOLS **************************** -bool FETerrain::IsBrushActive() -{ - return bBrushActive; -} - -void FETerrain::SetBrushActive(const bool NewValue) -{ - bBrushActive = NewValue; -} - -FE_TERRAIN_BRUSH_MODE FETerrain::GetBrushMode() -{ - return BrushMode; -} - -void FETerrain::SetBrushMode(const FE_TERRAIN_BRUSH_MODE NewValue) -{ - BrushMode = NewValue; -} - -float FETerrain::GetBrushSize() -{ - return BrushSize; -} - -void FETerrain::SetBrushSize(float NewValue) -{ - if (NewValue <= 0.0f) - NewValue = 0.001f; - - if (NewValue > 1000.0f) - NewValue = 1000.0f; - - BrushSize = NewValue; -} - -float FETerrain::GetBrushIntensity() -{ - return BrushIntensity; -} - -void FETerrain::SetBrushIntensity(float NewValue) -{ - if (NewValue <= 0.0f) - NewValue = 0.001f; - - if (NewValue > 1000.0f) - NewValue = 1000.0f; - - BrushIntensity = NewValue; -} - -void FETerrain::UpdateBrush(const glm::dvec3 MouseRayStart, const glm::dvec3 MouseRayDirection) -{ - if (BrushVisualFB == nullptr) - return; - - if (GetBrushMode() == FE_TERRAIN_BRUSH_NONE) - { - if (bBrushVisualFBCleared) - return; - - // get current clear color. - GLfloat BkColor[4]; - glGetFloatv(GL_COLOR_CLEAR_VALUE, BkColor); - - BrushVisualFB->Bind(); - FE_GL_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); - FE_GL_ERROR(glClear(GL_COLOR_BUFFER_BIT)); - BrushVisualFB->UnBind(); - bBrushVisualFBCleared = true; - - FE_GL_ERROR(glClearColor(BkColor[0], BkColor[1], BkColor[2], BkColor[3])); - - return; - } - - if (bCPUHeightInfoDirtyFlag) - { - if (std::chrono::duration_cast>(std::chrono::system_clock::now() - LastChangesTimeStamp).count() * 1000.0f > WaitBeforeUpdateMs) - { - bCPUHeightInfoDirtyFlag = false; - UpdateCpuHeightInfo(); - UpdateSnapedInstancedEntities(); - } - } - - const float Range = GetXSize() * 2.0f; - const glm::dvec3 CurrentTerrainPoint = GetPointOnTerrain(MouseRayStart, MouseRayDirection, 0, Range); - - float LocalX = static_cast(CurrentTerrainPoint.x); - float LocalZ = static_cast(CurrentTerrainPoint.z); - - LocalX -= GetAABB().GetMin()[0]; - LocalZ -= GetAABB().GetMin()[2]; - - LocalX = LocalX / GetXSize(); - LocalZ = LocalZ / GetZSize(); - - if (IsBrushActive()) - { - LastChangesTimeStamp = std::chrono::system_clock::now(); - bCPUHeightInfoDirtyFlag = true; - HeightMap->SetDirtyFlag(true); - - if (LocalX > 0 && LocalZ > 0 && LocalX < 1.0 && LocalZ < 1.0) - { - BrushOutputShader->UpdateParameterData("brushCenter", glm::vec2(LocalX, LocalZ)); - } - - BrushOutputShader->UpdateParameterData("brushSize", BrushSize / (GetXSize() * 2.0f)); - BrushOutputShader->UpdateParameterData("brushMode", static_cast(GetBrushMode())); - - BrushOutputFB->SetColorAttachment(HeightMap); - BrushOutputShader->UpdateParameterData("brushIntensity", BrushIntensity / 10.0f); - - BrushOutputFB->Bind(); - BrushOutputShader->Start(); - - if (BrushMode == FE_TERRAIN_BRUSH_LAYER_DRAW) - { - LayerMaps[0]->Bind(0); - FE_GL_ERROR(glViewport(0, 0, LayerMaps[0]->GetWidth(), LayerMaps[0]->GetHeight())); - - BrushOutputShader->UpdateParameterData("brushIntensity", BrushIntensity * 5.0f); - BrushOutputShader->UpdateParameterData("layerIndex", static_cast(GetBrushLayerIndex())); - BrushOutputFB->SetColorAttachment(LayerMaps[0]); - BrushOutputShader->LoadDataToGPU(); - - FE_GL_ERROR(glBindVertexArray(PlaneMesh->GetVaoID())); - FE_GL_ERROR(glEnableVertexAttribArray(0)); - FE_GL_ERROR(glDrawElements(GL_TRIANGLES, PlaneMesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); - - - LayerMaps[1]->Bind(0); - BrushOutputShader->UpdateParameterData("layerIndex", static_cast(GetBrushLayerIndex() - 4.0f)); - BrushOutputFB->SetColorAttachment(LayerMaps[1]); - BrushOutputShader->LoadDataToGPU(); - - FE_GL_ERROR(glDrawElements(GL_TRIANGLES, PlaneMesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); - FE_GL_ERROR(glDisableVertexAttribArray(0)); - FE_GL_ERROR(glBindVertexArray(0)); - - BrushOutputShader->Stop(); - - // Normalize all layers values to add up to 1.0 - LayersNormalizeShader->Start(); - LayerMaps[0]->Bind(0); - LayerMaps[1]->Bind(1); - - BrushOutputFB->SetColorAttachment(LayerMaps[0], 0); - BrushOutputFB->SetColorAttachment(LayerMaps[1], 1); - - FE_GL_ERROR(glBindVertexArray(PlaneMesh->GetVaoID())); - FE_GL_ERROR(glEnableVertexAttribArray(0)); - FE_GL_ERROR(glDrawElements(GL_TRIANGLES, PlaneMesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); - FE_GL_ERROR(glDisableVertexAttribArray(0)); - FE_GL_ERROR(glBindVertexArray(0)); - - LayersNormalizeShader->Stop(); - } - else - { - BrushOutputShader->LoadDataToGPU(); - HeightMap->Bind(0); - - FE_GL_ERROR(glViewport(0, 0, HeightMap->GetWidth(), HeightMap->GetHeight())); - - FE_GL_ERROR(glBindVertexArray(PlaneMesh->GetVaoID())); - FE_GL_ERROR(glEnableVertexAttribArray(0)); - FE_GL_ERROR(glDrawElements(GL_TRIANGLES, PlaneMesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); - FE_GL_ERROR(glDisableVertexAttribArray(0)); - FE_GL_ERROR(glBindVertexArray(0)); - - HeightMap->UnBind(); - } - - BrushOutputShader->Stop(); - BrushOutputFB->UnBind(); - } - - if (LocalX > 0 && LocalZ > 0 && LocalX < 1.0 && LocalZ < 1.0) - { - BrushVisualShader->UpdateParameterData("brushCenter", glm::vec2(LocalX, LocalZ)); - } - BrushVisualShader->UpdateParameterData("brushSize", BrushSize / (GetXSize() * 2.0f)); - - BrushVisualFB->Bind(); - BrushVisualShader->Start(); - - BrushVisualShader->LoadDataToGPU(); - FE_GL_ERROR(glViewport(0, 0, HeightMap->GetWidth(), HeightMap->GetHeight())); - - FE_GL_ERROR(glBindVertexArray(PlaneMesh->GetVaoID())); - FE_GL_ERROR(glEnableVertexAttribArray(0)); - FE_GL_ERROR(glDrawElements(GL_TRIANGLES, PlaneMesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); - FE_GL_ERROR(glDisableVertexAttribArray(0)); - FE_GL_ERROR(glBindVertexArray(0)); - - BrushVisualShader->Stop(); - BrushVisualFB->UnBind(); - - bBrushVisualFBCleared = false; -} -// **************************** TERRAIN EDITOR TOOLS END **************************** - -void FETerrain::SnapInstancedEntity(FEEntityInstanced* EntityToSnap) -{ - EntityToSnap->SnapToTerrain(this, &FETerrain::GetHeightAt); - SnapedInstancedEntities.push_back(EntityToSnap); -} - -void FETerrain::UpdateSnapedInstancedEntities() -{ - for (size_t i = 0; i < SnapedInstancedEntities.size(); i++) - { - // safety check - if (SnapedInstancedEntities[i] == nullptr) - continue; - // if entity is still snapped - if (SnapedInstancedEntities[i]->GetSnappedToTerrain() != this) - continue; - - SnapedInstancedEntities[i]->Clear(); - SnapedInstancedEntities[i]->Populate(SnapedInstancedEntities[i]->SpawnInfo); - } -} - -void FETerrain::UnSnapInstancedEntity(FEEntityInstanced* EntityToUnSnap) -{ - for (size_t i = 0; i < SnapedInstancedEntities.size(); i++) - { - if (SnapedInstancedEntities[i] == EntityToUnSnap) - { - SnapedInstancedEntities.erase(SnapedInstancedEntities.begin() + i); - break; - } - } - - EntityToUnSnap->UnSnapFromTerrain(); -} - -bool FETerrain::GetNextEmptyLayerSlot(size_t& NextEmptyLayerIndex) -{ - for (size_t i = 0; i < Layers.size(); i++) - { - if (Layers[i] == nullptr) - { - NextEmptyLayerIndex = i; - return true; - } - } - - return false; -} - -FETerrainLayer* FETerrain::ActivateVacantLayerSlot(FEMaterial* Material) -{ - size_t LayerIndex = 0; - if (!GetNextEmptyLayerSlot(LayerIndex)) - { - LOG.Add("FETerrain::activateLayerSlot was not able to acquire vacant layer index", "FE_LOG_RENDERING", FE_LOG_WARNING); - return nullptr; - } - - if (Material == nullptr) - { - LOG.Add("FETerrain::activateLayerSlot material is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); - return nullptr; - } - - if (!Material->IsCompackPacking()) - { - LOG.Add("FETerrain::activateLayerSlot material is not compactly packed", "FE_LOG_RENDERING", FE_LOG_WARNING); - return nullptr; - } - - if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) - { - LOG.Add("FETerrain::activateLayerSlot with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); - return nullptr; - } - - if (Layers[LayerIndex] != nullptr) - { - LOG.Add("FETerrain::activateLayerSlot on indicated layer slot layer is already active", "FE_LOG_RENDERING", FE_LOG_WARNING); - return nullptr; - } - - Layers[LayerIndex] = new FETerrainLayer(std::string("Layer_") + std::to_string(LayerIndex)); - Layers[LayerIndex]->Material = Material; - - if (LayerIndex == 0) - SetWireframeMode(false); - - return Layers[LayerIndex]; -} - -FETerrainLayer* FETerrain::GetLayerInSlot(const size_t LayerIndex) -{ - if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) - { - LOG.Add("FETerrain::getLayerInSlot with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); - return nullptr; - } - - return Layers[LayerIndex]; -} - -void FETerrain::DeleteLayerInSlot(const size_t LayerIndex) -{ - if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) - { - LOG.Add("FETerrain::deleteLayerInSlot with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } - - if (Layers[LayerIndex] == nullptr) - { - LOG.Add("FETerrain::deleteLayerInSlot on indicated layer slot layer is already inactive", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } - - delete Layers[LayerIndex]; - Layers[LayerIndex] = nullptr; - - if (LayerIndex == 0 && Layers[LayerIndex + 1] == nullptr) - SetWireframeMode(true); - - if (LayerIndex + 1 >= FE_TERRAIN_MAX_LAYERS) - return; - - size_t CurrentIndex = LayerIndex + 1; - while (true) - { - if (CurrentIndex >= FE_TERRAIN_MAX_LAYERS || Layers[CurrentIndex] == nullptr) - return; - - Layers[CurrentIndex - 1] = Layers[CurrentIndex]; - Layers[CurrentIndex] = nullptr; - - CurrentIndex++; - } -} - -size_t FETerrain::GetBrushLayerIndex() -{ - return BrushLayerIndex; -} - -void FETerrain::SetBrushLayerIndex(const size_t NewValue) -{ - if (NewValue >= FE_TERRAIN_MAX_LAYERS) - return; - - BrushLayerIndex = NewValue; -} - -void FETerrain::LoadLayersDataToGPU() -{ - bool GPUDataIsStale = false; - for (size_t i = 0; i < Layers.size(); i++) - { - const size_t index = i * FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER; - if (Layers[i] != nullptr && Layers[i]->GetMaterial()->IsCompackPacking()) - { - const FEMaterial* CurrentMaterial = Layers[i]->GetMaterial(); - // normalMapIntensity - GPULayersData[index] = CurrentMaterial->GetNormalMapIntensity(); - // AOIntensity - GPULayersData[index + 1] = CurrentMaterial->GetAmbientOcclusionIntensity(); - // AOMapIntensity - GPULayersData[index + 2] = CurrentMaterial->GetAmbientOcclusionMapIntensity(); - // roughness - GPULayersData[index + 3] = CurrentMaterial->GetRoughness(); - // roughnessMapIntensity - GPULayersData[index + 4] = CurrentMaterial->GetRoughnessMapIntensity(); - // metalness - GPULayersData[index + 5] = CurrentMaterial->GetMetalness(); - // metalnessMapIntensity - GPULayersData[index + 6] = CurrentMaterial->GetMetalnessMapIntensity(); - // displacementMapIntensity - GPULayersData[index + 7] = CurrentMaterial->GetDisplacementMapIntensity(); - // tiling - GPULayersData[index + 8] = CurrentMaterial->GetTiling(); - } - else - { - for (size_t j = 0; j < FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER; j++) - { - GPULayersData[index + j] = -1.0f; - } - } - } - - for (size_t i = 0; i < FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * FE_TERRAIN_MAX_LAYERS; i++) - { - if (GPULayersData[i] != OldGPULayersData[i]) - { - GPUDataIsStale = true; - OldGPULayersData = GPULayersData; - break; - } - } - - if (GPUDataIsStale) - { - float* TerrainLayersDataPtr = static_cast(glMapNamedBufferRange(GPULayersDataBuffer, 0, - sizeof(float) * FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * - FE_TERRAIN_MAX_LAYERS, - GL_MAP_WRITE_BIT/* | - GL_MAP_INVALIDATE_BUFFER_BIT | - GL_MAP_UNSYNCHRONIZED_BIT*/)); - for (size_t i = 0; i < FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * FE_TERRAIN_MAX_LAYERS; i++) - { - TerrainLayersDataPtr[i] = GPULayersData[i]; - } - FE_GL_ERROR(glUnmapNamedBuffer(GPULayersDataBuffer)); - } -} - -int FETerrain::LayersUsed() -{ - int LayersUsed = 0; - for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) - { - if (GetLayerInSlot(i) == nullptr) - break; - LayersUsed++; - } - - return LayersUsed; -} - -bool FETerrain::UpdateLayerMapsRawData() -{ - if (TIME.EndTimeStamp(this->GetObjectID()) != -1.0) - { - if (TIME.EndTimeStamp(this->GetObjectID()) < 2000) - return false; - } - - TIME.BeginTimeStamp(this->GetObjectID()); - - for (size_t i = 0; i < LayerMaps.size(); i++) - { - if (LayerMapsRawData[i] != nullptr) - { - delete LayerMapsRawData[i]; - LayerMapsRawData[i] = nullptr; - } - - if (LayerMaps[i] == nullptr) - { - LayerMapsRawData[i] = nullptr; - } - else - { - LayerMapsRawData[i] = LayerMaps[i]->GetRawData(); - } - } - - return true; -} - -float FETerrain::GetLayerIntensityAt(glm::vec2 XZWorldPosition, const int LayerIndex) -{ - if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) - { - LOG.Add("FETerrain::getLayerIntensityAt with out of bound \"layerIndex\"", "FE_LOG_GENERAL", FE_LOG_WARNING); - return 0.0f; - } - - if (Layers[LayerIndex] == nullptr) - { - LOG.Add("FETerrain::getLayerIntensityAt on indicated layer slot layer is nullptr", "FE_LOG_GENERAL", FE_LOG_WARNING); - return 0.0f; - } - - UpdateLayerMapsRawData(); - - float LocalX = XZWorldPosition[0]; - float LocalZ = XZWorldPosition[1]; - - LocalX -= FinalAABB.GetMin()[0]; - LocalZ -= FinalAABB.GetMin()[2]; - - if (XSize == 0 || ZSize == 0) - GetAABB(); - - LocalX = LocalX / XSize; - LocalZ = LocalZ / ZSize; - - if (LocalX > 0 && LocalZ > 0 && LocalX < 1.0 && LocalZ < 1.0) - { - const int TextureIndex = LayerIndex / FE_TERRAIN_LAYER_PER_TEXTURE; - FETexture* Texture = this->LayerMaps[TextureIndex]; - LocalX = static_cast(static_cast(LocalX * Texture->GetWidth())); - LocalZ = static_cast(static_cast(LocalZ * Texture->GetHeight())); - - const int Index = static_cast(LocalZ * Texture->GetWidth() + LocalX) * 4 + LayerIndex % FE_TERRAIN_LAYER_PER_TEXTURE; - - if (LayerMapsRawData[TextureIndex] != nullptr) - return LayerMapsRawData[TextureIndex][Index] / 255.0f; - - } - - return 0.0f; -} - -void FETerrain::ConnectInstancedEntityToLayer(FEEntityInstanced* Entity, const int LayerIndex) -{ - if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) - { - LOG.Add("FETerrain::connectInstancedEntityToLayer with out of bound \"layerIndex\"", "FE_LOG_GENERAL", FE_LOG_WARNING); - return; - } - - if (Layers[LayerIndex] == nullptr) - { - LOG.Add("FETerrain::connectInstancedEntityToLayer on indicated layer slot layer is nullptr", "FE_LOG_GENERAL", FE_LOG_WARNING); - return; - } - - for (size_t i = 0; i < SnapedInstancedEntities.size(); i++) - { - if (SnapedInstancedEntities[i]->GetObjectID() == Entity->GetObjectID()) - { - Entity->ConnectToTerrainLayer(this, LayerIndex, &FETerrain::GetLayerIntensityAt); - break; - } - } -} - -void FETerrain::UnConnectInstancedEntityFromLayer(FEEntityInstanced* Entity) -{ - Entity->UnConnectFromTerrainLayer(); -} \ No newline at end of file diff --git a/Renderer/FETerrain.h b/Renderer/FETerrain.h deleted file mode 100644 index f2496ae..0000000 --- a/Renderer/FETerrain.h +++ /dev/null @@ -1,194 +0,0 @@ -#pragma once - -#ifndef FETERRAIN_H -#define FETERRAIN_H - -#include "../Renderer/FEEntityInstanced.h" - -namespace FocalEngine -{ -#define FE_TERRAIN_MAX_LAYERS 8 -#define FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER 9 -#define FE_TERRAIN_LAYER_PER_TEXTURE 4 -#define FE_TERRAIN_STANDARD_HIGHT_MAP_RESOLUTION 1024 -#define FE_TERRAIN_STANDARD_LAYER_MAP_RESOLUTION 512 - - class FETerrain; - - class FETerrainLayer : public FEObject - { - friend FETerrain; - friend FEResourceManager; - - FEMaterial* Material = nullptr; - FETerrainLayer(std::string Name); - public: - void SetMaterial(FEMaterial* NewValue); - FEMaterial* GetMaterial(); - }; - - enum FE_TERRAIN_BRUSH_MODE - { - FE_TERRAIN_BRUSH_NONE = 0, - FE_TERRAIN_BRUSH_SCULPT_DRAW = 1, - FE_TERRAIN_BRUSH_SCULPT_DRAW_INVERSED = 2, - FE_TERRAIN_BRUSH_SCULPT_LEVEL = 3, - FE_TERRAIN_BRUSH_SCULPT_SMOOTH = 4, - FE_TERRAIN_BRUSH_LAYER_DRAW = 5 - }; - - class FETerrain : public FEObject - { - friend FERenderer; - friend FEResourceManager; - public: - FETerrain(std::string Name); - ~FETerrain(); - - FETransformComponent Transform; - - void Render(); - - bool IsVisible(); - void SetVisibility(bool NewValue); - - FEAABB GetAABB(); - FEAABB GetPureAABB(); - - bool IsCastingShadows(); - void SetCastingShadows(bool NewValue); - - bool IsReceivingShadows(); - void SetReceivingShadows(bool NewValue); - - FEShader* Shader = nullptr; - - FETexture* HeightMap = nullptr; - std::vector LayerMaps; - FETexture* ProjectedMap = nullptr; - - bool IsWireframeMode(); - void SetWireframeMode(bool NewValue); - - float GetHightScale(); - void SetHightScale(float NewValue); - - float GetDisplacementScale(); - void SetDisplacementScale(float NewValue); - - glm::vec2 GetTileMult(); - void SetTileMult(glm::vec2 NewValue); - - float GetLODLevel(); - void SetLODLevel(float NewValue); - - float GetChunkPerSide(); - void SetChunkPerSide(float NewValue); - - float GetHeightAt(glm::vec2 XZWorldPosition); - - float GetXSize(); - float GetZSize(); - - // ********************************** PointOnTerrain ********************************** - glm::dvec3 GetPointOnTerrain(glm::dvec3 MouseRayStart, glm::dvec3 MouseRayDirection, float StartDistance = 0.0f, float EndDistance = 256.0f); - // ********************************** PointOnTerrain END ********************************** - - // **************************** TERRAIN EDITOR TOOLS **************************** - float GetBrushSize(); - void SetBrushSize(float NewValue); - - float GetBrushIntensity(); - void SetBrushIntensity(float NewValue); - - bool IsBrushActive(); - void SetBrushActive(bool NewValue); - - FE_TERRAIN_BRUSH_MODE GetBrushMode(); - void SetBrushMode(FE_TERRAIN_BRUSH_MODE NewValue); - - size_t GetBrushLayerIndex(); - void SetBrushLayerIndex(size_t NewValue); - // **************************** TERRAIN EDITOR TOOLS END **************************** - - void SnapInstancedEntity(FEEntityInstanced* EntityToSnap); - void UnSnapInstancedEntity(FEEntityInstanced* EntityToUnSnap); - - void ConnectInstancedEntityToLayer(FEEntityInstanced* Entity, int LayerIndex); - void UnConnectInstancedEntityFromLayer(FEEntityInstanced* Entity); - - bool GetNextEmptyLayerSlot(size_t& NextEmptyLayerIndex); - FETerrainLayer* GetLayerInSlot(size_t LayerIndex); - - int LayersUsed(); - - float GetLayerIntensityAt(glm::vec2 XZWorldPosition, int LayerIndex); - private: - bool bWireframeMode = false; - bool bVisible = true; - bool bCastShadows = true; - bool bReceiveShadows = true; - - float HightScale = 1.0f; - float DisplacementScale = 0.2f; - float ScaleFactor = 1.0f; - glm::vec2 TileMult = glm::vec2(1.0); - glm::vec2 HightMapShift = glm::vec2(0.0); - float ChunkPerSide = 2.0f; - - float LODLevel = 64.0f; - FEAABB AABB; - FEAABB FinalAABB; - float XSize = 0.0f; - float ZSize = 0.0f; - - std::vector HeightMapArray; - - // ********************************** PointOnTerrain ********************************** - glm::dvec3 BinarySearch(int Count, float Start, float Finish, glm::dvec3 MouseRayStart, glm::dvec3 MouseRayDirection); - bool IntersectionInRange(float Start, float Finish, glm::dvec3 MouseRayStart, glm::dvec3 MouseRayDirection); - glm::dvec3 GetPointOnRay(glm::dvec3 MouseRayStart, glm::dvec3 MouseRayDirection, float Distance); - bool IsUnderGround(glm::dvec3 TestPoint); - // ********************************** PointOnTerrain END ********************************** - - // **************************** TERRAIN EDITOR TOOLS **************************** - bool bBrushActive = false; - FE_TERRAIN_BRUSH_MODE BrushMode = FE_TERRAIN_BRUSH_NONE; - - size_t BrushLayerIndex = 0; - float BrushSize = 2.0f; - float BrushIntensity = 0.01f; - FEFramebuffer* BrushOutputFB = nullptr; - FEShader* BrushOutputShader = nullptr; - FEShader* LayersNormalizeShader = nullptr; - FEFramebuffer* BrushVisualFB = nullptr; - FEShader* BrushVisualShader = nullptr; - FEMesh* PlaneMesh = nullptr; - - void UpdateBrush(glm::dvec3 MouseRayStart, glm::dvec3 MouseRayDirection); - size_t WaitBeforeUpdateMs = 50; - std::chrono::system_clock::time_point LastChangesTimeStamp; - void UpdateCpuHeightInfo(); - void UpdateSnapedInstancedEntities(); - - bool bCPUHeightInfoDirtyFlag = false; - size_t FramesBeforeUpdate = 50; - bool bBrushVisualFBCleared = false; - // **************************** TERRAIN EDITOR TOOLS END **************************** - - std::vector SnapedInstancedEntities; - - std::vector Layers; - std::vector LayerMapsRawData; - bool UpdateLayerMapsRawData(); - FETerrainLayer* ActivateVacantLayerSlot(FEMaterial* Material); - void DeleteLayerInSlot(size_t LayerIndex); - - GLuint GPULayersDataBuffer = 0; - void LoadLayersDataToGPU(); - std::vector GPULayersData; - std::vector OldGPULayersData; - }; -} - -#endif FETERRAIN_H \ No newline at end of file diff --git a/Renderer/FETexture.cpp b/Renderer/FETexture.cpp index ff66f5d..0087ea8 100644 --- a/Renderer/FETexture.cpp +++ b/Renderer/FETexture.cpp @@ -101,7 +101,7 @@ void FETexture::GPUAllocateTeture(const GLenum Target, const GLint Level, const { FE_GL_ERROR(glTexImage2D(Target, Level, Internalformat, Width, Height, Border, Format, Type, Data)); #ifdef FE_GPUMEM_ALLOCATION_LOGING - FELOG::getInstance().logError("Texture creation with width: " + std::to_string(width) + " height: " + std::to_string(height)); + LOG.logError("Texture creation with width: " + std::to_string(width) + " height: " + std::to_string(height)); #endif } @@ -124,30 +124,30 @@ void FETexture::EraseFromOnDeleteCallBackList(const std::string ObjectID) std::string FETexture::TextureInternalFormatToString(const GLint InternalFormat) { - std::string result; + std::string Result; if (InternalFormat == GL_RGBA) { - result += "GL_RGBA"; + Result += "GL_RGBA"; } else if (InternalFormat == GL_RED) { - result += "GL_RED"; + Result += "GL_RED"; } else if (InternalFormat == GL_R16) { - result += "GL_R16"; + Result += "GL_R16"; } else if (InternalFormat == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) { - result += "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT"; + Result += "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT"; } else if (InternalFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { - result += "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT"; + Result += "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT"; } - return result; + return Result; } std::vector FETexture::NoDeletingList = std::vector(); @@ -158,60 +158,96 @@ void FETexture::AddToNoDeletingList(const GLuint TextureID) unsigned char* FETexture::GetRawData(size_t* RawDataSize) { - unsigned char* result = nullptr; + unsigned char* Result = nullptr; if (RawDataSize != nullptr) *RawDataSize = 0; if (InternalFormat != GL_RGBA && + InternalFormat != GL_RGB && InternalFormat != GL_RED && InternalFormat != GL_R16 && + InternalFormat != GL_RG16F && InternalFormat != GL_COMPRESSED_RGBA_S3TC_DXT5_EXT && InternalFormat != GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && - InternalFormat != GL_RGBA16F) + InternalFormat != GL_RGBA16F && + InternalFormat != GL_DEPTH24_STENCIL8 && + InternalFormat != GL_DEPTH_COMPONENT32) { - LOG.Add("FETexture::getRawData internalFormat is not supported", "FE_LOG_SAVING", FE_LOG_ERROR); - return result; + LOG.Add("FETexture::GetRawData internalFormat is not supported", "FE_LOG_SAVING", FE_LOG_ERROR); + return Result; } FE_GL_ERROR(glActiveTexture(GL_TEXTURE0)); FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, TextureID)); - if (InternalFormat == GL_RGBA16F) + if (InternalFormat == GL_RG16F) + { + if (RawDataSize != nullptr) + *RawDataSize = GetWidth() * GetHeight() * 2 * sizeof(unsigned short); + Result = new unsigned char[GetWidth() * GetHeight() * 2 * sizeof(unsigned short)]; + glPixelStorei(GL_PACK_ALIGNMENT, 2); + FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RG, GL_HALF_FLOAT, Result)); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + } + else if (InternalFormat == GL_DEPTH_COMPONENT32) + { + if (RawDataSize != nullptr) + *RawDataSize = GetWidth() * GetHeight() * sizeof(float); + Result = new unsigned char[GetWidth() * GetHeight() * sizeof(float)]; + FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GL_FLOAT, Result)); + } + else if (InternalFormat == GL_DEPTH24_STENCIL8) + { + if (RawDataSize != nullptr) + *RawDataSize = GetWidth() * GetHeight() * 4; + Result = new unsigned char[GetWidth() * GetHeight() * 4]; + FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, Result)); + } + else if (InternalFormat == GL_RGBA16F) { if (RawDataSize != nullptr) *RawDataSize = GetWidth() * GetHeight() * 4 * sizeof(unsigned short); - result = new unsigned char[GetWidth() * GetHeight() * 4 * sizeof(unsigned short)]; + + Result = new unsigned char[GetWidth() * GetHeight() * 4 * sizeof(unsigned short)]; glPixelStorei(GL_PACK_ALIGNMENT, 2); - FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_HALF_FLOAT, result)); + FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_HALF_FLOAT, Result)); glPixelStorei(GL_PACK_ALIGNMENT, 4); } else if (InternalFormat == GL_R16) { if (RawDataSize != nullptr) *RawDataSize = GetWidth() * GetHeight() * 2; - result = new unsigned char[GetWidth() * GetHeight() * 2]; + Result = new unsigned char[GetWidth() * GetHeight() * 2]; glPixelStorei(GL_PACK_ALIGNMENT, 2); - FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_SHORT, result)); + FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_SHORT, Result)); glPixelStorei(GL_PACK_ALIGNMENT, 4); } else if (InternalFormat == GL_RED) { if (RawDataSize != nullptr) *RawDataSize = GetWidth() * GetHeight(); - result = new unsigned char[GetWidth() * GetHeight()]; + Result = new unsigned char[GetWidth() * GetHeight()]; glPixelStorei(GL_PACK_ALIGNMENT, 1); - FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_BYTE, result)); + FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_BYTE, Result)); glPixelStorei(GL_PACK_ALIGNMENT, 4); } - else + // Check GL_COMPRESSED_RGBA_S3TC_DXT5_EXT and GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + else if (InternalFormat == GL_RGBA || InternalFormat == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT || InternalFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { if (RawDataSize != nullptr) *RawDataSize = GetWidth() * GetHeight() * 4; - result = new unsigned char[GetWidth() * GetHeight() * 4]; - FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, result)); + Result = new unsigned char[GetWidth() * GetHeight() * 4]; + FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, Result)); + } + else if (InternalFormat == GL_RGB) + { + if (RawDataSize != nullptr) + *RawDataSize = GetWidth() * GetHeight() * 3; + Result = new unsigned char[GetWidth() * GetHeight() * 3]; + FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, Result)); } - return result; + return Result; } void FETexture::UpdateRawData(unsigned char* NewRawData, const size_t MipCount) @@ -219,6 +255,7 @@ void FETexture::UpdateRawData(unsigned char* NewRawData, const size_t MipCount) if (InternalFormat != GL_RGBA && InternalFormat != GL_RED && InternalFormat != GL_R16 && + InternalFormat != GL_RGBA16F && InternalFormat != GL_COMPRESSED_RGBA_S3TC_DXT5_EXT && InternalFormat != GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { @@ -243,7 +280,14 @@ void FETexture::UpdateRawData(unsigned char* NewRawData, const size_t MipCount) FE_GL_ERROR(glGenTextures(1, &TextureID)); FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, TextureID)); - if (InternalFormat == GL_RGBA) + if (InternalFormat == GL_RGBA16F) + { + glPixelStorei(GL_UNPACK_ALIGNMENT, 2); + FE_GL_ERROR(glTexStorage2D(GL_TEXTURE_2D, static_cast(MipCount), GL_RGBA16F, GetWidth(), GetHeight())); + FE_GL_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GetWidth(), GetHeight(), GL_RGBA, GL_HALF_FLOAT, (void*)(NewRawData))); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + } + else if (InternalFormat == GL_RGBA) { FE_GL_ERROR(glTexStorage2D(GL_TEXTURE_2D, static_cast(MipCount), GL_RGBA8, GetWidth(), GetHeight())); FE_GL_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GetWidth(), GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, (void*)(NewRawData))); diff --git a/Renderer/FEViewport.cpp b/Renderer/FEViewport.cpp new file mode 100644 index 0000000..4f6c785 --- /dev/null +++ b/Renderer/FEViewport.cpp @@ -0,0 +1,47 @@ +#include "FEViewport.h" +using namespace FocalEngine; + +FEViewport::FEViewport() +{ + ID = APPLICATION.GetUniqueHexID(); +} + +std::string FEViewport::GetID() const +{ + return ID; +} + +FEViewportType FEViewport::GetType() const +{ + return Type; +} + +int FEViewport::GetX() const +{ + return X; +} + +int FEViewport::GetY() const +{ + return Y; +} + +void FEViewport::SetWidth(const int NewWidth) +{ + Width = NewWidth; +} + +void FEViewport::SetHeight(const int NewHeight) +{ + Height = NewHeight; +} + +int FEViewport::GetWidth() const +{ + return Width; +} + +int FEViewport::GetHeight() const +{ + return Height; +} \ No newline at end of file diff --git a/Renderer/FEViewport.h b/Renderer/FEViewport.h new file mode 100644 index 0000000..5050e4e --- /dev/null +++ b/Renderer/FEViewport.h @@ -0,0 +1,47 @@ +#pragma once +#include "FEFramebuffer.h" + +namespace FocalEngine +{ + enum FEViewportType + { + FE_VIEWPORT_VIRTUAL = 0, + FE_VIEWPORT_OS_WINDOW = 1, + FE_VIEWPORT_GLFW_WINDOW = 2, + FE_VIEWPORT_FEWINDOW = 3, + FE_VIEWPORT_IMGUI_WINDOW = 4 + }; + + class FEViewport + { + friend class FERenderer; + friend class FEngine; + friend class FECameraSystem; + friend struct FECameraComponent; + + FEViewport(); + + std::string ID = ""; + + int X = 0; + int Y = 0; + int Width = 0; + int Height = 0; + + void SetWidth(const int NewWidth); + void SetHeight(const int NewHeight); + + FEViewportType Type = FE_VIEWPORT_VIRTUAL; + void* WindowHandle = nullptr; + public: + std::string GetID() const; + + FEViewportType GetType() const; + + int GetX() const; + int GetY() const; + + int GetWidth() const; + int GetHeight() const; + }; +} \ No newline at end of file diff --git a/ResourceManager/FEObjLoader.cpp b/ResourceManager/FEObjLoader.cpp index 900f2cd..346ad3d 100644 --- a/ResourceManager/FEObjLoader.cpp +++ b/ResourceManager/FEObjLoader.cpp @@ -1,8 +1,6 @@ #include "FEObjLoader.h" using namespace FocalEngine; -FEObjLoader* FEObjLoader::Instance = nullptr; - FEObjLoader::FEObjLoader() { @@ -290,173 +288,54 @@ void FEObjLoader::ReadFile(const char* FileName) } } -glm::vec3 FEObjLoader::CalculateNormal(glm::dvec3 V0, glm::dvec3 V1, glm::dvec3 V2) -{ - glm::dvec3 Edge_0 = V2 - V1; - glm::dvec3 Edge_1 = V2 - V0; - - glm::dvec3 Normal = glm::normalize(glm::cross(Edge_1, Edge_0)); - - if (isnan(Normal.x) || isnan(Normal.y) || isnan(Normal.z)) - Normal = glm::dvec3(); - - return Normal; -} - void FEObjLoader::CalculateNormals(FERawOBJData* Data) { - std::vector TrianglesArea; std::vector TrianglePoints; TrianglePoints.resize(3); - for (size_t i = 0; i < Data->FInd.size(); i += 3) + std::vector Indices; + for (size_t i = 0; i < Data->FInd.size(); i++) { - int VertexPosition = Data->FInd[i] * 3; - TrianglePoints[0] = glm::dvec3(Data->DVerC[VertexPosition], Data->DVerC[VertexPosition + 1], Data->DVerC[VertexPosition + 2]); - - VertexPosition = Data->FInd[i + 1] * 3; - TrianglePoints[1] = glm::dvec3(Data->DVerC[VertexPosition], Data->DVerC[VertexPosition + 1], Data->DVerC[VertexPosition + 2]); - - VertexPosition = Data->FInd[i + 2] * 3; - TrianglePoints[2] = glm::dvec3(Data->DVerC[VertexPosition], Data->DVerC[VertexPosition + 1], Data->DVerC[VertexPosition + 2]); - - TrianglesArea.push_back(GEOMETRY.CalculateTriangleArea(TrianglePoints[0], TrianglePoints[1], TrianglePoints[2])); + Indices.push_back(Data->FInd[i]); } - - struct VertexNormalsInfo - { - std::vector Normals; - std::vector Areas; - double AreaSum = 0.0; - }; - - std::vector DataForWeightedNormals; - DataForWeightedNormals.resize(Data->FInd.size()); - int IndexShift = 3; - // We assume that there were no normals info read. - for (size_t i = 0; i < Data->FInd.size(); i += 3) + if (bUseDoublePrecisionForReadingCoordinates) { - glm::dvec3 V0 = { Data->DVerC[Data->FInd[i] * IndexShift], Data->DVerC[Data->FInd[i] * IndexShift + 1], Data->DVerC[Data->FInd[i] * IndexShift + 2] }; - glm::dvec3 V1 = { Data->DVerC[Data->FInd[i + 1] * IndexShift], Data->DVerC[Data->FInd[i + 1] * IndexShift + 1], Data->DVerC[Data->FInd[i + 1] * IndexShift + 2] }; - glm::dvec3 V2 = { Data->DVerC[Data->FInd[i + 2] * IndexShift], Data->DVerC[Data->FInd[i + 2] * IndexShift + 1], Data->DVerC[Data->FInd[i + 2] * IndexShift + 2] }; - - glm::vec3 Normal = CalculateNormal(V0, V1, V2); - - DataForWeightedNormals[Data->FInd[i]].Normals.push_back(Normal); - DataForWeightedNormals[Data->FInd[i]].Areas.push_back(TrianglesArea[i / 3]); - DataForWeightedNormals[Data->FInd[i]].AreaSum += TrianglesArea[i / 3]; - - DataForWeightedNormals[Data->FInd[i + 1]].Normals.push_back(Normal); - DataForWeightedNormals[Data->FInd[i + 1]].Areas.push_back(TrianglesArea[i / 3]); - DataForWeightedNormals[Data->FInd[i + 1]].AreaSum += TrianglesArea[i / 3]; - - DataForWeightedNormals[Data->FInd[i + 2]].Normals.push_back(Normal); - DataForWeightedNormals[Data->FInd[i + 2]].Areas.push_back(TrianglesArea[i / 3]); - DataForWeightedNormals[Data->FInd[i + 2]].AreaSum += TrianglesArea[i / 3]; + GEOMETRY.CalculateNormals(Data->FInd, Data->DVerC, Data->FNorC); } - - for (size_t i = 0; i < Data->FInd.size(); i += 3) + else { - glm::vec3 Normal = glm::vec3(0.0f); - for (size_t j = 0; j < DataForWeightedNormals[Data->FInd[i]].Normals.size(); j++) + std::vector DoubleVertices; + for (size_t i = 0; i < Data->FVerC.size(); i++) { - Normal += DataForWeightedNormals[Data->FInd[i]].Normals[j] * DataForWeightedNormals[Data->FInd[i]].Areas[j] / DataForWeightedNormals[Data->FInd[i]].AreaSum; + DoubleVertices.push_back(Data->FVerC[i]); } - Normal = glm::normalize(Normal); - if (isnan(Normal.x) || isnan(Normal.y) || isnan(Normal.z)) - Normal = glm::vec3(); - Data->FNorC[Data->FInd[i] * IndexShift] = Normal.x; - Data->FNorC[Data->FInd[i] * IndexShift + 1] = Normal.y; - Data->FNorC[Data->FInd[i] * IndexShift + 2] = Normal.z; - - Normal = glm::vec3(0.0f); - for (size_t j = 0; j < DataForWeightedNormals[Data->FInd[i + 1]].Normals.size(); j++) - { - Normal += DataForWeightedNormals[Data->FInd[i + 1]].Normals[j] * DataForWeightedNormals[Data->FInd[i + 1]].Areas[j] / DataForWeightedNormals[Data->FInd[i + 1]].AreaSum; - } - Normal = glm::normalize(Normal); - if (isnan(Normal.x) || isnan(Normal.y) || isnan(Normal.z)) - Normal = glm::vec3(); - - Data->FNorC[Data->FInd[i + 1] * IndexShift] = Normal.x; - Data->FNorC[Data->FInd[i + 1] * IndexShift + 1] = Normal.y; - Data->FNorC[Data->FInd[i + 1] * IndexShift + 2] = Normal.z; - - Normal = glm::vec3(0.0f); - for (size_t j = 0; j < DataForWeightedNormals[Data->FInd[i + 2]].Normals.size(); j++) - { - Normal += DataForWeightedNormals[Data->FInd[i + 2]].Normals[j] * DataForWeightedNormals[Data->FInd[i + 2]].Areas[j] / DataForWeightedNormals[Data->FInd[i + 2]].AreaSum; - } - Normal = glm::normalize(Normal); - if (isnan(Normal.x) || isnan(Normal.y) || isnan(Normal.z)) - Normal = glm::vec3(); - - Data->FNorC[Data->FInd[i + 2] * IndexShift] = Normal.x; - Data->FNorC[Data->FInd[i + 2] * IndexShift + 1] = Normal.y; - Data->FNorC[Data->FInd[i + 2] * IndexShift + 2] = Normal.z; + GEOMETRY.CalculateNormals(Data->FInd, DoubleVertices, Data->FNorC); } } -glm::vec3 FEObjLoader::CalculateTangent(const glm::vec3 V0, const glm::vec3 V1, const glm::vec3 V2, std::vector&& Textures) -{ - const glm::vec3 Q1 = V1 - V0; - const glm::vec3 Q2 = V2 - V0; - const glm::vec2 UV0 = Textures[0]; - const glm::vec2 UV1 = Textures[1]; - const glm::vec2 UV2 = Textures[2]; - - const float T1 = UV1.y - UV0.y; - const float T2 = UV2.y - UV0.y; - - const glm::vec3 Tangent = T1 * Q2 - T2 * Q1; - - return Tangent; -} - void FEObjLoader::CalculateTangents(FERawOBJData* Data) { - for (size_t i = 0; i < Data->FInd.size() - 1; i += 3) + std::vector Indices; + for (size_t i = 0; i < Data->FInd.size(); i++) + { + Indices.push_back(Data->FInd[i]); + } + + if (bUseDoublePrecisionForReadingCoordinates) + { + GEOMETRY.CalculateTangents(Data->FInd, Data->DVerC, Data->FTexC, Data->FNorC, Data->FTanC); + } + else { - const glm::vec3 V0 = { Data->FVerC[Data->FInd[i] * 3], Data->FVerC[Data->FInd[i] * 3 + 1], Data->FVerC[Data->FInd[i] * 3 + 2] }; - const glm::vec3 V1 = { Data->FVerC[Data->FInd[i + 1] * 3], Data->FVerC[Data->FInd[i + 1] * 3 + 1], Data->FVerC[Data->FInd[i + 1] * 3 + 2] }; - const glm::vec3 V2 = { Data->FVerC[Data->FInd[i + 2] * 3], Data->FVerC[Data->FInd[i + 2] * 3 + 1], Data->FVerC[Data->FInd[i + 2] * 3 + 2] }; - - glm::vec2 T0 = { Data->FTexC[Data->FInd[i] * 2], Data->FTexC[Data->FInd[i] * 2 + 1] }; - glm::vec2 T1 = { Data->FTexC[Data->FInd[i + 1] * 2], Data->FTexC[Data->FInd[i + 1] * 2 + 1] }; - glm::vec2 T2 = { Data->FTexC[Data->FInd[i + 2] * 2], Data->FTexC[Data->FInd[i + 2] * 2 + 1] }; - - glm::vec3 Tangent = CalculateTangent(V0, V1, V2, { T0, T1, T2 }); - // To eliminate NaN values after normalization. - // I encounter this problem if triangle has same texture coordinates. - if (Tangent.x != 0 || Tangent.y != 0 || Tangent.z != 0) + std::vector DoubleVertices; + for (size_t i = 0; i < Data->FVerC.size(); i++) { - Tangent = glm::normalize(Tangent); + DoubleVertices.push_back(Data->FVerC[i]); } - else - { - glm::vec3 Normal = { Data->FNorC[Data->FInd[i] * 3], Data->FNorC[Data->FInd[i] * 3 + 1], Data->FNorC[Data->FInd[i] * 3 + 2] }; - glm::vec3 TangentOne = glm::cross(Normal, glm::vec3(0.0f, 0.0f, 1.0f)); - glm::vec3 TangentTwo = glm::cross(Normal, glm::vec3(0.0f, 1.0f, 0.0f)); - // Choosing candidate with bigger length/magnitude. - // Length/magnitude of cross product depend on sine of angle between vectors - // and sine of 90 degrees is 1.0(max value), so basically we are choosing cross product in which vectors was closer to perpendicular(assuming both vectors are unit vectors). - Tangent = glm::length(TangentOne) > glm::length(TangentTwo) ? TangentOne : TangentTwo; - Tangent = glm::normalize(Tangent); - } - - Data->FTanC[Data->FInd[i] * 3] = Tangent.x; - Data->FTanC[Data->FInd[i] * 3 + 1] = Tangent.y; - Data->FTanC[Data->FInd[i] * 3 + 2] = Tangent.z; - - Data->FTanC[Data->FInd[i + 1] * 3] = Tangent.x; - Data->FTanC[Data->FInd[i + 1] * 3 + 1] = Tangent.y; - Data->FTanC[Data->FInd[i + 1] * 3 + 2] = Tangent.z; - - Data->FTanC[Data->FInd[i + 2] * 3] = Tangent.x; - Data->FTanC[Data->FInd[i + 2] * 3 + 1] = Tangent.y; - Data->FTanC[Data->FInd[i + 2] * 3 + 2] = Tangent.z; + + GEOMETRY.CalculateTangents(Data->FInd, DoubleVertices, Data->FTexC, Data->FNorC, Data->FTanC); } } @@ -578,7 +457,7 @@ void FEObjLoader::ProcessRawData(FERawOBJData* Data) } } - // After normalization we will cast double precision vertex coordinates to float precision. + // Convert normalized vertex coordinates from double to float if (bUseDoublePrecisionForReadingCoordinates) { Data->RawVertexCoordinates.resize(Data->RawVertexCoordinatesDoublePrecision.size()); @@ -861,7 +740,7 @@ void FEObjLoader::ReadMaterialFile(const char* OriginalOBJFile) std::string MaterialFileFullPath = FILE_SYSTEM.GetDirectoryPath(OriginalOBJFile); MaterialFileFullPath += MaterialFileName; - if (!FILE_SYSTEM.CheckFile(MaterialFileFullPath.c_str())) + if (!FILE_SYSTEM.DoesFileExist(MaterialFileFullPath.c_str())) { LOG.Add(std::string("material file: ") + MaterialFileName + " was indicated in OBJ file but this file can't be located.", "FE_LOG_LOADING", FE_LOG_ERROR); return; @@ -978,7 +857,7 @@ void FEObjLoader::ReadMaterialLine(std::stringstream& LineStream) if ((*StringEdited)[0] == ' ') StringEdited->erase(StringEdited->begin()); - if (!FILE_SYSTEM.CheckFile(StringEdited->c_str())) + if (!FILE_SYSTEM.DoesFileExist(StringEdited->c_str())) LookForFile(*StringEdited); } // Specular color texture map @@ -993,7 +872,7 @@ void FEObjLoader::ReadMaterialLine(std::stringstream& LineStream) if ((*StringEdited)[0] == ' ') StringEdited->erase(StringEdited->begin()); - if (!FILE_SYSTEM.CheckFile(StringEdited->c_str())) + if (!FILE_SYSTEM.DoesFileExist(StringEdited->c_str())) LookForFile(*StringEdited); } // Specular highlight component @@ -1008,7 +887,7 @@ void FEObjLoader::ReadMaterialLine(std::stringstream& LineStream) if ((*StringEdited)[0] == ' ') StringEdited->erase(StringEdited->begin()); - if (!FILE_SYSTEM.CheckFile(StringEdited->c_str())) + if (!FILE_SYSTEM.DoesFileExist(StringEdited->c_str())) LookForFile(*StringEdited); } // The alpha texture map @@ -1023,7 +902,7 @@ void FEObjLoader::ReadMaterialLine(std::stringstream& LineStream) if ((*StringEdited)[0] == ' ') StringEdited->erase(StringEdited->begin()); - if (!FILE_SYSTEM.CheckFile(StringEdited->c_str())) + if (!FILE_SYSTEM.DoesFileExist(StringEdited->c_str())) LookForFile(*StringEdited); } // Some implementations use 'map_bump' instead of 'bump' below @@ -1038,7 +917,7 @@ void FEObjLoader::ReadMaterialLine(std::stringstream& LineStream) if ((*StringEdited)[0] == ' ') StringEdited->erase(StringEdited->begin()); - if (!FILE_SYSTEM.CheckFile(StringEdited->c_str())) + if (!FILE_SYSTEM.DoesFileExist(StringEdited->c_str())) LookForFile(*StringEdited); } // Bump map (which by default uses luminance channel of the image) @@ -1053,7 +932,7 @@ void FEObjLoader::ReadMaterialLine(std::stringstream& LineStream) if ((*StringEdited)[0] == ' ') StringEdited->erase(StringEdited->begin()); - if (!FILE_SYSTEM.CheckFile(StringEdited->c_str())) + if (!FILE_SYSTEM.DoesFileExist(StringEdited->c_str())) LookForFile(*StringEdited); } // Displacement map @@ -1068,7 +947,7 @@ void FEObjLoader::ReadMaterialLine(std::stringstream& LineStream) if ((*StringEdited)[0] == ' ') StringEdited->erase(StringEdited->begin()); - if (!FILE_SYSTEM.CheckFile(StringEdited->c_str())) + if (!FILE_SYSTEM.DoesFileExist(StringEdited->c_str())) LookForFile(*StringEdited); } // Stencil decal texture (defaults to 'matte' channel of the image) @@ -1083,7 +962,7 @@ void FEObjLoader::ReadMaterialLine(std::stringstream& LineStream) if ((*StringEdited)[0] == ' ') StringEdited->erase(StringEdited->begin()); - if (!FILE_SYSTEM.CheckFile(StringEdited->c_str())) + if (!FILE_SYSTEM.DoesFileExist(StringEdited->c_str())) LookForFile(*StringEdited); } } @@ -1131,4 +1010,143 @@ void FEObjLoader::DoubleVertexOnSeams(bool NewValue) bool FEObjLoader::IsDoubleVertexOnSeams() { return bDoubleVertexOnSeams; +} + +bool FEObjLoader::SaveToOBJ(const char* FileName, FERawOBJData* Data) +{ + if (FileName == nullptr || Data == nullptr) + { + LOG.Add(std::string("Invalid parameters in function FEObjLoader::SaveToOBJ."), "FE_LOG_SAVING", FE_LOG_ERROR); + return false; + } + + std::ofstream File(FileName); + if (!File.is_open()) + { + LOG.Add(std::string("Failed to open file for writing in function FEObjLoader::SaveToOBJ: ") + FileName, "FE_LOG_SAVING", FE_LOG_ERROR); + return false; + } + + // Write header/comment + File << "# OBJ file exported by FocalEngine\n"; + File << "# " << Data->RawVertexCoordinates.size() << " vertices\n"; + File << "# " << Data->RawTextureCoordinates.size() << " texture coordinates\n"; + File << "# " << Data->RawNormalCoordinates.size() << " normals\n"; + File << "# " << Data->RawIndices.size() / 3 << " faces\n\n"; + + // Write material library reference if we have materials + if (!Data->MaterialRecords.empty()) + { + // Nothing to do here, we don't support writing MTL files + } + + // Write vertices + for (size_t i = 0; i < Data->RawVertexCoordinates.size(); i++) + { + File << "v " << Data->RawVertexCoordinates[i].x << " " + << Data->RawVertexCoordinates[i].y << " " + << Data->RawVertexCoordinates[i].z; + + // Add vertex colors if they exist + if (bHaveColors && i < Data->RawVertexColors.size()) + { + File << " " << Data->RawVertexColors[i].x << " " + << Data->RawVertexColors[i].y << " " + << Data->RawVertexColors[i].z; + } + + File << "\n"; + } + File << "\n"; + + // Write texture coordinates + if (bHaveTextureCoord) + { + for (size_t i = 0; i < Data->RawTextureCoordinates.size(); i++) + { + // OBJ format typically has Y flipped compared to OpenGL + File << "vt " << Data->RawTextureCoordinates[i].x << " " + << (1.0f - Data->RawTextureCoordinates[i].y) << "\n"; + } + File << "\n"; + } + + // Write normals + if (bHaveNormalCoord) + { + for (size_t i = 0; i < Data->RawNormalCoordinates.size(); i++) + { + File << "vn " << Data->RawNormalCoordinates[i].x << " " + << Data->RawNormalCoordinates[i].y << " " + << Data->RawNormalCoordinates[i].z << "\n"; + } + File << "\n"; + } + + size_t FaceCount = Data->RawIndices.size(); + for (size_t i = 0; i < FaceCount; i += 3) + { + // Write face indices + // OBJ indices are 1-based, our internal indices are 0-based + File << "f "; + + // Handle different formats based on what data we have + if (bHaveTextureCoord && bHaveNormalCoord) + { + // Format: v/vt/vn + for (size_t j = 0; j < 3; j++) + { + if (i + j < FaceCount) + { + int CurrentIndex = Data->RawIndices[i + j]; + int CurrentTextureIndex = Data->UVIndices[i + j]; + int CurrentNormalIndex = Data->NormalIndices[i + j]; + File << CurrentIndex << "/" << CurrentTextureIndex << "/" << CurrentNormalIndex << " "; + } + } + } + else if (bHaveTextureCoord && !bHaveNormalCoord) + { + // Format: v/vt + for (size_t j = 0; j < 3; j++) + { + if (i + j < FaceCount) + { + int CurrentIndex = Data->RawIndices[i + j]; + int CurrentTextureIndex = Data->UVIndices[i + j]; + File << CurrentIndex << "/" << CurrentTextureIndex << " "; + } + } + } + else if (!bHaveTextureCoord && bHaveNormalCoord) + { + // Format: v//vn + for (size_t j = 0; j < 3; j++) + { + if (i + j < FaceCount) + { + int CurrentIndex = Data->RawIndices[i + j]; + int CurrentNormalIndex = Data->NormalIndices[i + j]; + File << CurrentIndex << "//" << CurrentNormalIndex << " "; + } + } + } + else + { + // Format: v + for (size_t j = 0; j < 3; j++) + { + if (i + j < FaceCount) + { + File << (Data->RawIndices[i + j]) << " "; + } + } + } + + File << "\n"; + } + + File.close(); + LOG.Add(std::string("Successfully saved OBJ file: ") + FileName, "FE_LOG_SAVING", FE_LOG_INFO); + return true; } \ No newline at end of file diff --git a/ResourceManager/FEObjLoader.h b/ResourceManager/FEObjLoader.h index c032f5e..114c787 100644 --- a/ResourceManager/FEObjLoader.h +++ b/ResourceManager/FEObjLoader.h @@ -1,6 +1,6 @@ #pragma once -#include "../SubSystems/FEFileSystem.h" +#include "../SubSystems/FileSystem/FEFileSystem.h" #include "../Core/FEGeometricTools.h" namespace FocalEngine @@ -57,6 +57,10 @@ namespace FocalEngine // material records std::vector MaterialRecords; std::vector MatIDs; + + // Used for saving + std::vector NormalIndices; + std::vector UVIndices; }; class FEObjLoader @@ -82,6 +86,8 @@ namespace FocalEngine // Use to get raw data from the loaded file. // Recommenede only if you know what you are doing. std::vector* GetLoadedObjects(); + + bool SaveToOBJ(const char* FileName, FERawOBJData* Data); private: SINGLETON_PRIVATE_PART(FEObjLoader) @@ -102,13 +108,10 @@ namespace FocalEngine void ReadMaterialLine(std::stringstream& LineStream); bool CheckCurrentMaterialObject(); - void ReadLine(std::stringstream& lineStream, FERawOBJData* data); + void ReadLine(std::stringstream& LineStream, FERawOBJData* Data); void ProcessRawData(FERawOBJData* Data); - glm::vec3 CalculateNormal(glm::dvec3 V0, glm::dvec3 V1, glm::dvec3 V2); void CalculateNormals(FERawOBJData* Data); - - glm::vec3 CalculateTangent(const glm::vec3 V0, const glm::vec3 V1, const glm::vec3 V2, std::vector&& Textures); void CalculateTangents(FERawOBJData* Data); void NormalizeVertexPositions(FERawOBJData* Data); diff --git a/ResourceManager/FEResourceManager.cpp b/ResourceManager/FEResourceManager.cpp index 1282313..829d3bf 100644 --- a/ResourceManager/FEResourceManager.cpp +++ b/ResourceManager/FEResourceManager.cpp @@ -1,8 +1,14 @@ #define STB_IMAGE_IMPLEMENTATION #include "FEResourceManager.h" +#include "../SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptProject.h" using namespace FocalEngine; -FEResourceManager* FEResourceManager::Instance = nullptr; +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetResourceManager() +{ + return FEResourceManager::GetInstancePointer(); +} +#endif FETexture* FEResourceManager::CreateTexture(std::string Name, const std::string ForceObjectID) { @@ -17,24 +23,33 @@ FETexture* FEResourceManager::CreateTexture(std::string Name, const std::string return NewTexture; } -bool FEResourceManager::MakeTextureStandard(FETexture* Texture) +bool FEResourceManager::SetTag(FEObject* Object, std::string NewTag) { - if (Texture == nullptr) + if (Object == nullptr) { - LOG.Add("texture is nullptr in function FEResourceManager::makeTextureStandard.", "FE_LOG_GENERAL", FE_LOG_ERROR); + LOG.Add("Object is nullptr in function FEResourceManager::SetTag.", "FE_LOG_GENERAL", FE_LOG_ERROR); return false; } - if (StandardTextures.find(Texture->GetObjectID()) == StandardTextures.end()) + if (NewTag == ENGINE_RESOURCE_TAG) { - if (Textures.find(Texture->GetObjectID()) != Textures.end()) - Textures.erase(Texture->GetObjectID()); - StandardTextures[Texture->GetObjectID()] = Texture; + LOG.Add("Can't set tag to ENGINE_RESOURCE_TAG in function FEResourceManager::SetTag.", "FE_LOG_GENERAL", FE_LOG_ERROR); + return false; + } + + SetTagIternal(Object, NewTag); + return true; +} - return true; +void FEResourceManager::SetTagIternal(FEObject* Object, std::string NewTag) +{ + if (Object == nullptr) + { + LOG.Add("Object is nullptr in function FEResourceManager::SetTagIternal.", "FE_LOG_GENERAL", FE_LOG_ERROR); + return; } - return false; + Object->SetTag(NewTag); } FEMesh* FEResourceManager::CreateMesh(const GLuint VaoID, const unsigned int VertexCount, const int VertexBuffersTypes, const FEAABB AABB, std::string Name) @@ -49,49 +64,34 @@ FEMesh* FEResourceManager::CreateMesh(const GLuint VaoID, const unsigned int Ver return NewMesh; } -bool FEResourceManager::MakeMeshStandard(FEMesh* Mesh) +FETexture* FEResourceManager::LoadPNGTexture(const char* FileName, const std::string Name) { - if (Mesh == nullptr) - { - LOG.Add("mesh is nullptr in function FEResourceManager::makeMeshStandard.", "FE_LOG_GENERAL", FE_LOG_ERROR); - return false; - } - - if (StandardMeshes.find(Mesh->GetObjectID()) == StandardMeshes.end()) + std::vector RawFileData; + std::ifstream File(FileName, std::ios::binary); + if (!File) { - if (Meshes.find(Mesh->GetObjectID()) != Meshes.end()) - Meshes.erase(Mesh->GetObjectID()); - StandardMeshes[Mesh->GetObjectID()] = Mesh; - - return true; + LOG.Add(std::string("Can't load file: ") + FileName + " in function FEResourceManager::LoadPNGTexture.", "FE_LOG_LOADING", FE_LOG_ERROR); + return GetTexture("48271F005A73241F5D7E7134"); // "noTexture" } - return false; -} + File.unsetf(std::ios::skipws); + RawFileData.insert(RawFileData.begin(), std::istream_iterator(File), std::istream_iterator()); -FETexture* FEResourceManager::LoadPNGTexture(const char* FileName, const std::string Name) -{ - std::vector RawData; - unsigned UWidth, UHeight; + std::vector RawExtractedData; + unsigned int Width, Height; + lodepng::State State; - lodepng::decode(RawData, UWidth, UHeight, FileName); - if (RawData.empty()) + unsigned int Error = lodepng::decode(RawExtractedData, Width, Height, State, RawFileData); + if (Error != 0) { - LOG.Add(std::string("can't load file: ") + FileName + " in function FEResourceManager::LoadPNGTexture.", "FE_LOG_LOADING", FE_LOG_ERROR); - if (!StandardTextures.empty()) - { - return GetTexture("48271F005A73241F5D7E7134"); // "noTexture" - } - else - { - return nullptr; - } + LOG.Add(std::string("Can't load file: ") + FileName + " in function FEResourceManager::LoadPNGTexture.", "FE_LOG_LOADING", FE_LOG_ERROR); + return GetTexture("48271F005A73241F5D7E7134"); // "noTexture" } bool bUsingAlpha = false; - for (size_t i = 3; i < RawData.size(); i+=4) + for (size_t i = 3; i < RawExtractedData.size(); i += 4) { - if (RawData[i] != 255) + if (RawExtractedData[i] != 255) { bUsingAlpha = true; break; @@ -99,40 +99,69 @@ FETexture* FEResourceManager::LoadPNGTexture(const char* FileName, const std::st } FETexture* NewTexture = CreateTexture(Name); - NewTexture->Width = UWidth; - NewTexture->Height = UHeight; + NewTexture->Width = Width; + NewTexture->Height = Height; - const int InternalFormat = bUsingAlpha ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + if (State.info_png.color.bitdepth == 16 && State.info_png.color.colortype == LCT_GREY) + { + // Using lodepng second time to decode to a proper format. + RawExtractedData.clear(); + lodepng::State NewState; + NewState.info_raw.colortype = LCT_GREY; + NewState.info_raw.bitdepth = 16; + lodepng::decode(RawExtractedData, Width, Height, NewState, (unsigned char*)RawFileData.data(), RawFileData.size()); - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); - FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, InternalFormat, NewTexture->Width, NewTexture->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, RawData.data()); - NewTexture->InternalFormat = InternalFormat; + NewTexture->InternalFormat = GL_R16; + NewTexture->MagFilter = FE_LINEAR; + NewTexture->FileName = FileName; - if (NewTexture->MipEnabled) - { - FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); // to-do: fix this - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); - } + FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); + // lodepng returns 16-bit data with different bytes order that OpenGL expects. + FE_GL_ERROR(glPixelStorei(GL_UNPACK_SWAP_BYTES, TRUE)); + FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, NewTexture->InternalFormat, NewTexture->Width, NewTexture->Height, 0, GL_RED, GL_UNSIGNED_SHORT, RawExtractedData.data()); + FE_GL_ERROR(glPixelStorei(GL_UNPACK_SWAP_BYTES, FALSE)); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)); - if (NewTexture->MagFilter == FE_LINEAR) - { + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); } else { - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + NewTexture->InternalFormat = bUsingAlpha ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + + FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); + FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, NewTexture->InternalFormat, NewTexture->Width, NewTexture->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, RawExtractedData.data()); + + if (NewTexture->MipEnabled) + { + FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); + // TO-DO: make it configurable. + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); + } + + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)); + if (NewTexture->MagFilter == FE_LINEAR) + { + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + } + else + { + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + } } + NewTexture->FileName = FileName; if (Name.empty()) { const std::string FilePath = NewTexture->FileName; - std::size_t index = FilePath.find_last_of("/\\"); - const std::string NewFileName = FilePath.substr(index + 1); - index = NewFileName.find_last_of("."); - const std::string FileNameWithOutExtention = NewFileName.substr(0, index); + std::size_t Index = FilePath.find_last_of("/\\"); + const std::string NewFileName = FilePath.substr(Index + 1); + Index = NewFileName.find_last_of("."); + const std::string FileNameWithOutExtention = NewFileName.substr(0, Index); NewTexture->SetName(FileNameWithOutExtention); } @@ -144,36 +173,27 @@ void FEResourceManager::SaveFETexture(FETexture* Texture, const char* FileName) FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, Texture->TextureID)); GLint ImgSize = 0; - std::fstream file; - - file.open(FileName, std::ios::out | std::ios::binary); - // version of FETexture file type - float version = FE_TEXTURE_VERSION; - file.write((char*)&version, sizeof(float)); - - int ObjectIDSize = static_cast(Texture->GetObjectID().size() + 1); - file.write((char*)&ObjectIDSize, sizeof(int)); - file.write((char*)Texture->GetObjectID().c_str(), sizeof(char) * ObjectIDSize); + std::fstream File; + File.open(FileName, std::ios::out | std::ios::binary); - file.write((char*)&Texture->Width, sizeof(int)); - file.write((char*)&Texture->Height, sizeof(int)); - file.write((char*)&Texture->InternalFormat, sizeof(int)); + // Version of FETexture File type + float Version = FE_TEXTURE_VERSION; + File.write((char*)&Version, sizeof(float)); - int NameSize = static_cast(Texture->GetName().size() + 1); - file.write((char*)&NameSize, sizeof(int)); + OBJECT_MANAGER.SaveFEObjectPart(File, Texture); - char* TextureName = new char[NameSize]; - strcpy_s(TextureName, NameSize, Texture->GetName().c_str()); - file.write((char*)TextureName, sizeof(char) * NameSize); + File.write((char*)&Texture->Width, sizeof(int)); + File.write((char*)&Texture->Height, sizeof(int)); + File.write((char*)&Texture->InternalFormat, sizeof(int)); if (Texture->InternalFormat == GL_R16 || Texture->InternalFormat == GL_RED || Texture->InternalFormat == GL_RGBA) { size_t DataSize = 0; unsigned char* Pixels = Texture->GetRawData(&DataSize); - file.write((char*)&DataSize, sizeof(int)); - file.write((char*)Pixels, sizeof(char) * DataSize); - file.close(); + File.write((char*)&DataSize, sizeof(int)); + File.write((char*)Pixels, sizeof(char) * DataSize); + File.close(); delete[] Pixels; return; @@ -181,7 +201,7 @@ void FEResourceManager::SaveFETexture(FETexture* Texture, const char* FileName) const int MaxDimention = std::max(Texture->Width, Texture->Height); const size_t MipCount = static_cast(floor(log2(MaxDimention)) + 1); - char** PixelData = new char*[MipCount]; + char** PixelData = new char* [MipCount]; for (size_t i = 0; i < MipCount; i++) { @@ -203,7 +223,7 @@ void FEResourceManager::SaveFETexture(FETexture* Texture, const char* FileName) FE_GL_ERROR(glGetCompressedTexImage(GL_TEXTURE_2D, static_cast(i), AdditionalTestPixels)); int RealSize = 0; - for (size_t j = ImgSize * 2 - 1; j > 0 ; j--) + for (size_t j = ImgSize * 2 - 1; j > 0; j--) { if (Pixels[j] != ' ') { @@ -229,11 +249,11 @@ void FEResourceManager::SaveFETexture(FETexture* Texture, const char* FileName) delete[] Pixels; delete[] AdditionalTestPixels; - file.write((char*)&RealSize, sizeof(int)); - file.write((char*)PixelData[i], sizeof(char) * RealSize); + File.write((char*)&RealSize, sizeof(int)); + File.write((char*)PixelData[i], sizeof(char) * RealSize); } - file.close(); + File.close(); for (size_t i = 0; i < MipCount; i++) { @@ -268,11 +288,12 @@ FETexture* FEResourceManager::RawDataToFETexture(unsigned char* TextureData, con FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, NewTexture->InternalFormat, NewTexture->Width, NewTexture->Height, 0, Format, GL_UNSIGNED_BYTE, TextureData); - + if (NewTexture->MipEnabled) { FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f));// to-do: fix this + // TO-DO: make it configurable. + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); } @@ -304,9 +325,9 @@ struct LoadTextureAsyncInfo void LoadTextureFileAsync(void* InputData, void* OutputData) { const LoadTextureAsyncInfo* Input = reinterpret_cast(InputData); - std::fstream file; - file.open(Input->FileName.c_str(), std::ios::in | std::ios::binary | std::ios::ate); - const std::streamsize FileSize = file.tellg(); + std::fstream File; + File.open(Input->FileName.c_str(), std::ios::in | std::ios::binary | std::ios::ate); + const std::streamsize FileSize = File.tellg(); if (FileSize <= 0) { LoadTextureAsyncInfo* Output = reinterpret_cast(OutputData); @@ -317,10 +338,10 @@ void LoadTextureFileAsync(void* InputData, void* OutputData) return; } - file.seekg(0, std::ios::beg); + File.seekg(0, std::ios::beg); char* FileData = new char[static_cast(FileSize)]; - file.read(FileData, FileSize); - file.close(); + File.read(FileData, FileSize); + File.close(); LoadTextureAsyncInfo* Output = reinterpret_cast(OutputData); Output->FileData = FileData; @@ -333,29 +354,31 @@ void FEResourceManager::LoadTextureFileAsyncCallBack(void* OutputData) { const LoadTextureAsyncInfo* Input = reinterpret_cast(OutputData); - // File was not found, or it can't be read. + // File was not found, or TextureIterator can't be read. if (Input->FileData == nullptr) { // Get info about problematic texture. const FETexture* NotLoadedTexture = Input->NewTexture; // We will spill out error into a log. - LOG.Add("FEResourceManager::updateAsyncLoadedResources texture with ID: " + NotLoadedTexture->GetObjectID() + " was not loaded!", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("FEResourceManager::LoadTextureFileAsyncCallBack texture with ID: " + NotLoadedTexture->GetObjectID() + " was not loaded!", "FE_LOG_LOADING", FE_LOG_ERROR); // And delete entry for that texture in a general list of textures. - // That will prevent it from saving in a scene file. + // That will prevent TextureIterator from saving in a scene File. RESOURCE_MANAGER.DeleteFETexture(NotLoadedTexture); - //textures.erase(notLoadedTexture->getObjectID()); } else { const FETexture* NewlyCreatedTexture = RESOURCE_MANAGER.LoadFETexture(Input->FileData, "", Input->NewTexture); - // If some material uses this texture we should set dirty flag. - // Game model will updated as a consequences. - const std::vector MaterialList = RESOURCE_MANAGER.GetMaterialList(); + // If any material uses this texture, set the dirty flag. + // Game model will be updated as a consequence. + const std::vector MaterialList = RESOURCE_MANAGER.GetMaterialIDList(); - for (size_t p = 0; p < MaterialList.size(); p++) + for (size_t i = 0; i < MaterialList.size(); i++) { - FEMaterial* CurrentMaterial = RESOURCE_MANAGER.GetMaterial(MaterialList[p]); + FEMaterial* CurrentMaterial = RESOURCE_MANAGER.GetMaterial(MaterialList[i]); + if (CurrentMaterial->GetTag() == ENGINE_RESOURCE_TAG) + continue; + if (CurrentMaterial->IsTextureInList(NewlyCreatedTexture)) CurrentMaterial->SetDirtyFlag(true); } @@ -387,90 +410,82 @@ FETexture* FEResourceManager::LoadFETextureAsync(const char* FileName, const std FETexture* FEResourceManager::LoadFETexture(const char* FileName, const std::string Name, FETexture* ExistingTexture) { - std::fstream file; - file.open(FileName, std::ios::in | std::ios::binary | std::ios::ate); - const std::streamsize FileSize = file.tellg(); + std::fstream File; + File.open(FileName, std::ios::in | std::ios::binary | std::ios::ate); + const std::streamsize FileSize = File.tellg(); if (FileSize < 0) { LOG.Add(std::string("can't load file: ") + FileName + " in function FEResourceManager::LoadFETexture.", "FE_LOG_LOADING", FE_LOG_ERROR); return this->NoTexture; } - - file.seekg(0, std::ios::beg); + + File.seekg(0, std::ios::beg); char* FileData = new char[static_cast(FileSize)]; - file.read(FileData, FileSize); - file.close(); + File.read(FileData, FileSize); + File.close(); - return LoadFETexture(FileData, Name, ExistingTexture); + FETexture* Result = LoadFETexture(FileData, Name, ExistingTexture); + delete[] FileData; + + return Result; } FETexture* FEResourceManager::LoadFETexture(char* FileData, std::string Name, FETexture* ExistingTexture) { int CurrentShift = 0; - // version of FETexture file type - const float version = *(float*)(&FileData[CurrentShift]); + // Version of FETexture File type + const float Version = *(float*)(&FileData[CurrentShift]); CurrentShift += 4; - if (version != FE_TEXTURE_VERSION) + + int Width = 0; + int Height = 0; + int InternalFormat = 0; + char* TextureName = nullptr; + std::string NameFromFile; + + char* ObjectID = nullptr; + std::string ID; + + if (Version != FE_TEXTURE_VERSION) { LOG.Add(std::string("can't load fileData: in function FEResourceManager::LoadFETexture. FileData was created in different version of engine!"), "FE_LOG_LOADING", FE_LOG_ERROR); - if (!StandardTextures.empty()) - { - return GetTexture("48271F005A73241F5D7E7134"); // "noTexture" - } - else - { - return nullptr; - } + return GetTexture("48271F005A73241F5D7E7134"); // "noTexture" } - const int ObjectIDSize = *(int*)(&FileData[CurrentShift]); - CurrentShift += 4; - - char* ObjectID = new char[ObjectIDSize]; - strcpy_s(ObjectID, ObjectIDSize, (char*)(&FileData[CurrentShift])); - CurrentShift += ObjectIDSize; + FEObjectLoadedData ObjectData = OBJECT_MANAGER.LoadFEObjectPart(FileData, CurrentShift); + ID = ObjectData.ID; + NameFromFile = ObjectData.Name; - const int width = *(int*)(&FileData[CurrentShift]); - CurrentShift += 4; - const int height = *(int*)(&FileData[CurrentShift]); + Width = *(int*)(&FileData[CurrentShift]); CurrentShift += 4; - const int InternalFormat = *(int*)(&FileData[CurrentShift]); + Height = *(int*)(&FileData[CurrentShift]); CurrentShift += 4; - - const int NameSize = *(int*)(&FileData[CurrentShift]); + InternalFormat = *(int*)(&FileData[CurrentShift]); CurrentShift += 4; - char* TextureName = new char[NameSize]; - strcpy_s(TextureName, NameSize, (char*)(&FileData[CurrentShift])); - CurrentShift += NameSize; - FETexture* NewTexture = nullptr; if (ExistingTexture != nullptr) { NewTexture = ExistingTexture; - NewTexture->SetName(TextureName); + NewTexture->SetName(NameFromFile); FE_GL_ERROR(glGenTextures(1, &NewTexture->TextureID)); } else { - NewTexture = CreateTexture(TextureName); + NewTexture = CreateTexture(NameFromFile); } - NewTexture->Width = width; - NewTexture->Height = height; + NewTexture->Width = Width; + NewTexture->Height = Height; NewTexture->InternalFormat = InternalFormat; - // Height map should not be loaded by this function - if (NewTexture->InternalFormat == GL_R16) - return nullptr; - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); - if (NewTexture->InternalFormat == GL_RED || NewTexture->InternalFormat == GL_RGBA) + if (NewTexture->InternalFormat == GL_RED || NewTexture->InternalFormat == GL_RGBA || NewTexture->InternalFormat == GL_R16) { - int size = *(int*)(&FileData[CurrentShift]); + int Size = *(int*)(&FileData[CurrentShift]); CurrentShift += 4; - + NewTexture->UpdateRawData((unsigned char*)(&FileData[CurrentShift])); } else @@ -483,16 +498,16 @@ FETexture* FEResourceManager::LoadFETexture(char* FileData, std::string Name, FE int MipH = NewTexture->Height / 2; for (size_t i = 0; i < MipCount; i++) { - const int size = *(int*)(&FileData[CurrentShift]); + const int Size = *(int*)(&FileData[CurrentShift]); CurrentShift += 4; if (i == 0) { - FE_GL_ERROR(glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, NewTexture->Width, NewTexture->Height, NewTexture->InternalFormat, size, static_cast(&FileData[CurrentShift]))); + FE_GL_ERROR(glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, NewTexture->Width, NewTexture->Height, NewTexture->InternalFormat, Size, static_cast(&FileData[CurrentShift]))); } else { - FE_GL_ERROR(glCompressedTexSubImage2D(GL_TEXTURE_2D, static_cast(i), 0, 0, MipW, MipH, NewTexture->InternalFormat, size, static_cast(&FileData[CurrentShift]))); + FE_GL_ERROR(glCompressedTexSubImage2D(GL_TEXTURE_2D, static_cast(i), 0, 0, MipW, MipH, NewTexture->InternalFormat, Size, static_cast(&FileData[CurrentShift]))); MipW = MipW / 2; MipH = MipH / 2; @@ -501,7 +516,7 @@ FETexture* FEResourceManager::LoadFETexture(char* FileData, std::string Name, FE break; } - CurrentShift += size; + CurrentShift += Size; } } @@ -515,148 +530,19 @@ FETexture* FEResourceManager::LoadFETexture(char* FileData, std::string Name, FE FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); } - // Height map should not be loaded by this function - if (NewTexture->InternalFormat == GL_R16) - return nullptr; - if (NewTexture->MipEnabled) { //FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); // to-do: fix this + // TO-DO: make it configurable. + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); } - // overwrite objectID with objectID from file. - if (ObjectID != nullptr) - { - const std::string OldID = NewTexture->GetObjectID(); - NewTexture->SetID(ObjectID); - - if (Textures.find(OldID) != Textures.end()) - { - Textures.erase(OldID); - Textures[NewTexture->GetObjectID()] = NewTexture; - } - } - - delete[] ObjectID; - delete[] TextureName; - - return NewTexture; -} - -FETexture* FEResourceManager::LoadFEHeightmap(const char* FileName, FETerrain* Terrain, const std::string Name) -{ - std::fstream file; - file.open(FileName, std::ios::in | std::ios::binary | std::ios::ate); - const std::streamsize FileSize = file.tellg(); - if (FileSize < 0) - { - LOG.Add(std::string("can't load file: ") + FileName + " in function FEResourceManager::LoadFETexture.", "FE_LOG_LOADING", FE_LOG_ERROR); - Terrain->HeightMap = this->NoTexture; - return this->NoTexture; - } - - file.seekg(0, std::ios::beg); - char* FileData = new char[static_cast(FileSize)]; - file.read(FileData, FileSize); - file.close(); - - int CurrentShift = 0; - // version of FETexture file type - const float version = *(float*)(&FileData[CurrentShift]); - CurrentShift += 4; - if (version != FE_TEXTURE_VERSION) - { - LOG.Add(std::string("can't load file: ") + FileName + " in function FEResourceManager::LoadFETexture. File was created in different version of engine!", "FE_LOG_LOADING", FE_LOG_ERROR); - if (!StandardTextures.empty()) - { - return GetTexture("48271F005A73241F5D7E7134"); // "noTexture" - } - else - { - return nullptr; - } - } - - const int ObjectIDSize = *(int*)(&FileData[CurrentShift]); - CurrentShift += 4; - - char* ObjectID = new char[ObjectIDSize]; - strcpy_s(ObjectID, ObjectIDSize, (char*)(&FileData[CurrentShift])); - CurrentShift += ObjectIDSize; - - const int width = *(int*)(&FileData[CurrentShift]); - CurrentShift += 4; - const int height = *(int*)(&FileData[CurrentShift]); - CurrentShift += 4; - const int InternalFormat = *(int*)(&FileData[CurrentShift]); - CurrentShift += 4; - - const int NameSize = *(int*)(&FileData[CurrentShift]); - CurrentShift += 4; - - char* TextureName = new char[NameSize]; - strcpy_s(TextureName, NameSize, (char*)(&FileData[CurrentShift])); - CurrentShift += NameSize; - - FETexture* NewTexture = !Name.empty() ? CreateTexture(Name.c_str()) : CreateTexture(TextureName); - NewTexture->Width = width; - NewTexture->Height = height; - NewTexture->InternalFormat = InternalFormat; - NewTexture->FileName = FileName; - - const int size = *(int*)(&FileData[CurrentShift]); - CurrentShift += 4; - - // Reformating terrain from old saves. - /*for (size_t i = currentShift; i < currentShift + width * height * 2; i += 2) - { - *(unsigned short*)(&fileData[i]) += 0xffff * 0.5; - }*/ - - Terrain->HeightMapArray.resize(size / sizeof(unsigned short)); - float max = FLT_MIN; - float min = FLT_MAX; - for (size_t i = 0; i < size / sizeof(unsigned short); i++) - { - const unsigned short temp = *(unsigned short*)(&FileData[CurrentShift]); - Terrain->HeightMapArray[i] = temp / static_cast(0xFFFF); - CurrentShift += sizeof(unsigned short); - - if (max < Terrain->HeightMapArray[i]) - max = Terrain->HeightMapArray[i]; - - if (min > Terrain->HeightMapArray[i]) - min = Terrain->HeightMapArray[i]; - } - - CurrentShift -= size; - const glm::vec3 MinPoint = glm::vec3(-1.0f, min, -1.0f); - const glm::vec3 MaxPoint = glm::vec3(1.0f, max, 1.0f); - Terrain->AABB = FEAABB(MinPoint, MaxPoint); - - NewTexture->UpdateRawData((unsigned char*)(&FileData[CurrentShift])); - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); - - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)); - if (NewTexture->MagFilter == FE_LINEAR) - { - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - } - else - { - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); - } - - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - - // overwrite objectID with objectID from file. - if (ObjectID != nullptr) + // Overwrite objectID with objectID from File. + if (!ID.empty()) { const std::string OldID = NewTexture->GetObjectID(); - NewTexture->SetID(ObjectID); + NewTexture->SetID(ID); if (Textures.find(OldID) != Textures.end()) { @@ -665,13 +551,7 @@ FETexture* FEResourceManager::LoadFEHeightmap(const char* FileName, FETerrain* T } } - delete[] ObjectID; - delete[] FileData; - delete[] TextureName; - - Terrain->HeightMap = NewTexture; - InitTerrainEditTools(Terrain); - Terrain->UpdateCpuHeightInfo(); + NewTexture->Tag = ObjectData.Tag; return NewTexture; } @@ -688,13 +568,13 @@ FEMesh* FEResourceManager::RawDataToMesh(std::vector& Positions, std::vec } FEMesh* FEResourceManager::RawDataToMesh(float* Positions, const int PosSize, - float* UV, const int UVSize, - float* Normals, const int NormSize, - float* Tangents, const int TanSize, - int* Indices, const int IndexSize, - float* Colors, int ColorSize, - float* MatIndexs, const int MatIndexsSize, const int MatCount, - const std::string Name) + float* UV, const int UVSize, + float* Normals, const int NormSize, + float* Tangents, const int TanSize, + int* Indices, const int IndexSize, + float* Colors, int ColorSize, + float* MatIndexs, const int MatIndexsSize, const int MatCount, + const std::string Name) { int VertexType = FE_POSITION | FE_INDEX; @@ -703,7 +583,7 @@ FEMesh* FEResourceManager::RawDataToMesh(float* Positions, const int PosSize, FE_GL_ERROR(glBindVertexArray(VaoID)); GLuint IndicesBufferID; - // index + // Index FE_GL_ERROR(glGenBuffers(1, &IndicesBufferID)); FE_GL_ERROR(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndicesBufferID)); FE_GL_ERROR(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * IndexSize, Indices, GL_STATIC_DRAW)); @@ -808,6 +688,166 @@ FEMesh* FEResourceManager::RawDataToMesh(float* Positions, const int PosSize, return NewMesh; } +bool FEResourceManager::ExportFEMeshToOBJ(FEMesh* MeshToExport, const char* FileName) +{ + if (MeshToExport == nullptr) + { + LOG.Add("MeshToExport is nullptr in function FEResourceManager::ExportFEMeshToOBJ.", "FE_LOG_SAVING", FE_LOG_ERROR); + return false; + } + + if (FileName == nullptr) + { + LOG.Add("FileName is nullptr in function FEResourceManager::ExportFEMeshToOBJ.", "FE_LOG_SAVING", FE_LOG_ERROR); + return false; + } + + FEObjLoader& OBJLoader = FEObjLoader::GetInstance(); + // Create a raw OBJ data object to hold the mesh data + FERawOBJData Data; + + // Extract data from the mesh + const size_t VertexCount = MeshToExport->GetPositionsCount() / 3; + const size_t NormalCount = MeshToExport->GetNormalsCount() / 3; + const size_t UVCount = MeshToExport->GetUVCount() / 2; + const size_t IndexCount = MeshToExport->GetIndicesCount(); + const size_t ColorCount = MeshToExport->GetColorCount() / 3; + + OBJLoader.bHaveNormalCoord = NormalCount > 0 ? true : false; + OBJLoader.bHaveTextureCoord = UVCount > 0 ? true : false; + OBJLoader.bHaveColors = ColorCount > 0 ? true : false; + + // Get buffer data from GPU + float* Positions = new float[MeshToExport->GetPositionsCount()]; + FE_GL_ERROR(glGetNamedBufferSubData(MeshToExport->GetPositionsBufferID(), 0, sizeof(float) * MeshToExport->GetPositionsCount(), Positions)); + + float* Normals = nullptr; + if (NormalCount > 0) + { + Normals = new float[MeshToExport->GetNormalsCount()]; + FE_GL_ERROR(glGetNamedBufferSubData(MeshToExport->GetNormalsBufferID(), 0, sizeof(float) * MeshToExport->GetNormalsCount(), Normals)); + } + + float* UVs = nullptr; + if (UVCount > 0) + { + UVs = new float[MeshToExport->GetUVCount()]; + FE_GL_ERROR(glGetNamedBufferSubData(MeshToExport->GetUVBufferID(), 0, sizeof(float) * MeshToExport->GetUVCount(), UVs)); + } + + int* Indices = new int[MeshToExport->GetIndicesCount()]; + FE_GL_ERROR(glGetNamedBufferSubData(MeshToExport->GetIndicesBufferID(), 0, sizeof(int) * MeshToExport->GetIndicesCount(), Indices)); + + float* Colors = nullptr; + if (ColorCount > 0) + { + Colors = new float[MeshToExport->GetColorCount()]; + FE_GL_ERROR(glGetNamedBufferSubData(MeshToExport->GetColorBufferID(), 0, sizeof(float) * MeshToExport->GetColorCount(), Colors)); + } + + float* MaterialIndices = nullptr; + if (MeshToExport->GetMaterialsIndicesCount() > 0) + { + MaterialIndices = new float[MeshToExport->GetMaterialsIndicesCount()]; + FE_GL_ERROR(glGetNamedBufferSubData(MeshToExport->GetMaterialsIndicesBufferID(), 0, sizeof(float) * MeshToExport->GetMaterialsIndicesCount(), MaterialIndices)); + } + + // Fill RawOBJData with vertex positions + for (size_t i = 0; i < VertexCount; i++) + { + glm::vec3 VertexPositions(Positions[i * 3], Positions[i * 3 + 1], Positions[i * 3 + 2]); + Data.RawVertexCoordinates.push_back(VertexPositions); + + if (Colors != nullptr) + { + glm::vec3 Color(Colors[i * 3], Colors[i * 3 + 1], Colors[i * 3 + 2]); + Data.RawVertexColors.push_back(Color); + } + } + + // Fill RawOBJData with texture coordinates (UVs) + if (UVs != nullptr) + { + for (size_t i = 0; i < UVCount; i++) + { + glm::vec2 UV(UVs[i * 2], UVs[i * 2 + 1]); + // Flip V coordinate to follow OBJ convention + //UV.y = 1.0f - UV.y; + Data.RawTextureCoordinates.push_back(UV); + } + } + + // Fill RawOBJData with normal coordinates + if (Normals != nullptr) + { + for (size_t i = 0; i < NormalCount; i++) + { + glm::vec3 NormalVector(Normals[i * 3], Normals[i * 3 + 1], Normals[i * 3 + 2]); + Data.RawNormalCoordinates.push_back(NormalVector); + } + } + + // Create material record if needed + if (MeshToExport->MaterialsCount > 0 && MaterialIndices != nullptr) + { + // Default material record as placeholder + MaterialRecord MaterialRecord; + MaterialRecord.Name = MeshToExport->GetName() + "_material"; + Data.MaterialRecords.push_back(MaterialRecord); + } + + // Convert indices to the OBJ format (1-based, not 0-based) + // OBJ format requires indices for vertex/uv/normal for each face + for (size_t i = 0; i < IndexCount; i += 3) + { + // For each triangle's vertex + for (size_t j = 0; j < 3; j++) + { + int CurrentIndex = Indices[i + j] + 1; // +1 because OBJ indices start at 1 + + // In OBJ, each face vertex is defined as v/vt/vn + Data.RawIndices.push_back(CurrentIndex); // vertex position index + + if (UVCount > 0) + Data.UVIndices.push_back(CurrentIndex); // texture coordinate index + + if (NormalCount > 0) + Data.NormalIndices.push_back(CurrentIndex); // normal vector index + } + } + + if (Colors != nullptr) + { + for (size_t i = 0; i < ColorCount; i += 3) + { + glm::vec3 Color(Colors[i], Colors[i + 1], Colors[i + 2]); + Data.RawVertexColors.push_back(Color); + } + } + + // Use FEObjLoader to save the file + bool Result = OBJLoader.SaveToOBJ(FileName, &Data); + + // Clean up allocated memory + delete[] Positions; + if (Normals) delete[] Normals; + if (UVs) delete[] UVs; + delete[] Indices; + if (Colors) delete[] Colors; + if (MaterialIndices) delete[] MaterialIndices; + + if (Result) + { + LOG.Add(std::string("Successfully exported mesh to: ") + FileName, "FE_LOG_SAVING", FE_LOG_INFO); + } + else + { + LOG.Add(std::string("Failed to export mesh to: ") + FileName, "FE_LOG_SAVING", FE_LOG_ERROR); + } + + return Result; +} + void FEResourceManager::LoadStandardMeshes() { if (Meshes.find("84251E6E0D0801363579317R"/*"cube"*/) != Meshes.end()) @@ -878,10 +918,12 @@ void FEResourceManager::LoadStandardMeshes() 0.625f, 0.25f }; - StandardMeshes["84251E6E0D0801363579317R"] = RawDataToMesh(CubePositions, CubeNormals, CubeTangents, CubeUV, CubeIndices, "cube"); - Meshes.erase(StandardMeshes["84251E6E0D0801363579317R"/*"cube"*/]->GetObjectID()); - StandardMeshes["84251E6E0D0801363579317R"/*"cube"*/]->SetID("84251E6E0D0801363579317R"/*"cube"*/); - StandardMeshes["84251E6E0D0801363579317R"/*"cube"*/]->SetName("FECube"); + FEMesh* NewMesh = RawDataToMesh(CubePositions, CubeNormals, CubeTangents, CubeUV, CubeIndices, "cube"); + Meshes.erase(NewMesh->GetObjectID()); + NewMesh->SetID("84251E6E0D0801363579317R"/*"cube"*/); + NewMesh->SetName("FECube"); + NewMesh->SetTag(ENGINE_RESOURCE_TAG); + Meshes[NewMesh->GetObjectID()] = NewMesh; std::vector PlaneIndices = { 0, 1, 2, 3, 0, 2 @@ -905,33 +947,61 @@ void FEResourceManager::LoadStandardMeshes() 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; - StandardMeshes["1Y251E6E6T78013635793156"] = RawDataToMesh(PlanePositions, PlaneNormals, PlaneTangents, PlaneUV, PlaneIndices, "plane"); - Meshes.erase(StandardMeshes["1Y251E6E6T78013635793156"/*"plane"*/]->GetObjectID()); - StandardMeshes["1Y251E6E6T78013635793156"/*"plane"*/]->SetID("1Y251E6E6T78013635793156"/*"plane"*/); - StandardMeshes["1Y251E6E6T78013635793156"/*"plane"*/]->SetName("FEPlane"); + NewMesh = RawDataToMesh(PlanePositions, PlaneNormals, PlaneTangents, PlaneUV, PlaneIndices, "plane"); + Meshes.erase(NewMesh->GetObjectID()); + NewMesh->SetID("1Y251E6E6T78013635793156"/*"plane"*/); + NewMesh->SetName("FEPlane"); + NewMesh->SetTag(ENGINE_RESOURCE_TAG); + Meshes[NewMesh->GetObjectID()] = NewMesh; - StandardMeshes["7F251E3E0D08013E3579315F"] = LoadFEMesh((ResourcesFolder + "7F251E3E0D08013E3579315F.model").c_str(), "sphere"); - Meshes.erase(StandardMeshes["7F251E3E0D08013E3579315F"/*"sphere"*/]->GetObjectID()); - StandardMeshes["7F251E3E0D08013E3579315F"/*"sphere"*/]->SetID("7F251E3E0D08013E3579315F"/*"sphere"*/); - StandardMeshes["7F251E3E0D08013E3579315F"/*"sphere"*/]->SetName("FESphere"); + NewMesh = LoadFEMesh((ResourcesFolder + "7F251E3E0D08013E3579315F.model").c_str(), "sphere"); + Meshes.erase(NewMesh->GetObjectID()); + NewMesh->SetID("7F251E3E0D08013E3579315F"/*"sphere"*/); + NewMesh->SetName("FESphere"); + NewMesh->SetTag(ENGINE_RESOURCE_TAG); + Meshes[NewMesh->GetObjectID()] = NewMesh; } FEResourceManager::FEResourceManager() { glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &MaxColorAttachments); + + if (FILE_SYSTEM.DoesFileExist(FILE_SYSTEM.GetCurrentWorkingPath() + "/EngineResources.fepackage")) + { + PrivateEngineAssetPackage = new FEAssetPackage(); + if (!PrivateEngineAssetPackage->LoadFromFile((FILE_SYSTEM.GetCurrentWorkingPath() + "/EngineResources.fepackage").c_str())) + { + LOG.Add("FEResourceManager::FEResourceManager: Can't load EngineResources.fepackage file!", "FE_LOG_LOADING", FE_LOG_ERROR); + delete PrivateEngineAssetPackage; + PrivateEngineAssetPackage = nullptr; + } + + UnPackPrivateEngineAssetPackage(PrivateEngineAssetPackage, FILE_SYSTEM.GetCurrentWorkingPath()); + } + NoTexture = LoadFETexture((ResourcesFolder + "48271F005A73241F5D7E7134.texture").c_str(), "noTexture"); - MakeTextureStandard(NoTexture); + NoTexture->SetTag(ENGINE_RESOURCE_TAG); FETexture::AddToNoDeletingList(NoTexture->GetTextureID()); FEShader* NewShader = CreateShader("FECombineFrameBuffers", LoadGLSL((EngineFolder + "CoreExtensions//PostProcessEffects//FE_ScreenQuad_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//PostProcessEffects//FE_CombineFrameBuffers_FS.glsl").c_str()).c_str()); - NewShader->SetID("5C267A01466A545E7D1A2E66"/*FECombineFrameBuffers*/); - MakeShaderStandard(NewShader); + LoadGLSL((EngineFolder + "CoreExtensions//PostProcessEffects//FE_CombineFrameBuffers_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "5C267A01466A545E7D1A2E66"); + NewShader->SetTag(ENGINE_RESOURCE_TAG); LoadStandardMaterial(); LoadStandardMeshes(); LoadStandardGameModels(); - LoadStandardPrefabs(); + + // Load all standard script modules. + std::vector PotentialScriptModuleFiles = FILE_SYSTEM.GetFileNamesInDirectory(ResourcesFolder); + for (size_t i = 0; i < PotentialScriptModuleFiles.size(); i++) + { + if (PotentialScriptModuleFiles[i].substr(PotentialScriptModuleFiles[i].size() - 19, 19) == ".nativescriptmodule") + { + LoadFENativeScriptModule((ResourcesFolder + PotentialScriptModuleFiles[i]).c_str()); + } + } } FEResourceManager::~FEResourceManager() @@ -941,33 +1011,33 @@ FEResourceManager::~FEResourceManager() std::vector FEResourceManager::ImportOBJ(const char* FileName, const bool bForceOneMesh) { - FEObjLoader& OBJLoader = FEObjLoader::getInstance(); + FEObjLoader& OBJLoader = FEObjLoader::GetInstance(); OBJLoader.bForceOneMesh = bForceOneMesh; OBJLoader.ReadFile(FileName); - std::vector result; + std::vector Result; for (size_t i = 0; i < OBJLoader.LoadedObjects.size(); i++) { const std::string name = GetFileNameFromFilePath(FileName) + "_" + std::to_string(i); - result.push_back(RawDataToMesh(OBJLoader.LoadedObjects[i]->FVerC.data(), static_cast(OBJLoader.LoadedObjects[i]->FVerC.size()), - OBJLoader.LoadedObjects[i]->FTexC.data(), static_cast(OBJLoader.LoadedObjects[i]->FTexC.size()), - OBJLoader.LoadedObjects[i]->FNorC.data(), static_cast(OBJLoader.LoadedObjects[i]->FNorC.size()), - OBJLoader.LoadedObjects[i]->FTanC.data(), static_cast(OBJLoader.LoadedObjects[i]->FTanC.size()), - OBJLoader.LoadedObjects[i]->FInd.data(), static_cast(OBJLoader.LoadedObjects[i]->FInd.size()), - nullptr, 0, - OBJLoader.LoadedObjects[i]->MatIDs.data(), static_cast(OBJLoader.LoadedObjects[i]->MatIDs.size()), static_cast(OBJLoader.LoadedObjects[i]->MaterialRecords.size()), name)); - - - // in rawDataToMesh() hidden FEMesh allocation and it will go to hash table so we need to use setMeshName() not setName. - result.back()->SetName(name); - Meshes[result.back()->GetObjectID()] = reinterpret_cast(result.back()); + Result.push_back(RawDataToMesh(OBJLoader.LoadedObjects[i]->FVerC.data(), static_cast(OBJLoader.LoadedObjects[i]->FVerC.size()), + OBJLoader.LoadedObjects[i]->FTexC.data(), static_cast(OBJLoader.LoadedObjects[i]->FTexC.size()), + OBJLoader.LoadedObjects[i]->FNorC.data(), static_cast(OBJLoader.LoadedObjects[i]->FNorC.size()), + OBJLoader.LoadedObjects[i]->FTanC.data(), static_cast(OBJLoader.LoadedObjects[i]->FTanC.size()), + OBJLoader.LoadedObjects[i]->FInd.data(), static_cast(OBJLoader.LoadedObjects[i]->FInd.size()), + nullptr, 0, + OBJLoader.LoadedObjects[i]->MatIDs.data(), static_cast(OBJLoader.LoadedObjects[i]->MatIDs.size()), static_cast(OBJLoader.LoadedObjects[i]->MaterialRecords.size()), name)); + + + // in rawDataToMesh() hidden FEMesh allocation and TextureIterator will go to hash table so we need to use setMeshName() not setName. + Result.back()->SetName(name); + Meshes[Result.back()->GetObjectID()] = reinterpret_cast(Result.back()); } - CreateMaterialsFromOBJData(result); + CreateMaterialsFromOBJData(Result); - return result; + return Result; } FEMesh* FEResourceManager::LoadFEMesh(const char* FileName, const std::string Name) @@ -979,47 +1049,27 @@ FEMesh* FEResourceManager::LoadFEMesh(const char* FileName, const std::string Na if (FileSize < 0) { LOG.Add(std::string("can't load file: ") + FileName + " in function FEResourceManager::LoadFEMesh.", "FE_LOG_LOADING", FE_LOG_ERROR); - if (!StandardMeshes.empty()) - { - return GetMesh("84251E6E0D0801363579317R"/*"cube"*/); - } - else - { - return nullptr; - } + return GetMesh("84251E6E0D0801363579317R"/*"cube"*/); } char* Buffer = new char[4]; - // version of FEMesh file type + // Version of FEMesh File type File.read(Buffer, 4); const float Version = *(float*)Buffer; + + std::string LoadedObjectID; + std::string LoadedName; if (Version != FE_MESH_VERSION) { LOG.Add(std::string("can't load file: ") + FileName + " in function FEResourceManager::LoadFEMesh. File was created in different version of engine!", "FE_LOG_LOADING", FE_LOG_ERROR); - if (!StandardMeshes.empty()) - { - return GetMesh("84251E6E0D0801363579317R"/*"cube"*/); - } - else - { - return nullptr; - } + return GetMesh("84251E6E0D0801363579317R"/*"cube"*/); } - File.read(Buffer, 4); - const int ObjectIDSize = *(int*)Buffer; - - char* ObjectID = new char[ObjectIDSize + 1]; - File.read(ObjectID, ObjectIDSize); - ObjectID[ObjectIDSize] = '\0'; + FEObjectLoadedData ObjectData = OBJECT_MANAGER.LoadFEObjectPart(File); + LoadedObjectID = ObjectData.ID; + LoadedName = ObjectData.Name; - File.read(Buffer, 4); - const int MeshNameSize = *(int*)Buffer; - char* MeshName = new char[MeshNameSize + 1]; - File.read(MeshName, MeshNameSize); - MeshName[MeshNameSize] = '\0'; - File.read(Buffer, 4); const int VertexCout = *(int*)Buffer; char* VertexBuffer = new char[VertexCout * 4]; @@ -1075,23 +1125,23 @@ FEMesh* FEResourceManager::LoadFEMesh(const char* FileName, const std::string Na File.close(); FEMesh* NewMesh = RawDataToMesh((float*)VertexBuffer, VertexCout, - (float*)TexBuffer, TexCout, - (float*)NormBuffer, NormCout, - (float*)TangBuffer, TangCout, - (int*)IndexBuffer, IndexCout, - nullptr, 0, - (float*)MatIndexBuffer, MatIndexCout, MatCount, - Name); + (float*)TexBuffer, TexCout, + (float*)NormBuffer, NormCout, + (float*)TangBuffer, TangCout, + (int*)IndexBuffer, IndexCout, + nullptr, 0, + (float*)MatIndexBuffer, MatIndexCout, MatCount, + Name.empty() ? LoadedName : Name); const std::string OldID = NewMesh->ID; - // overwrite objectID with objectID from file. - if (ObjectID != nullptr) + // Overwrite ID with Loaded ID. + if (!LoadedObjectID.empty()) { - NewMesh->SetID(ObjectID); + NewMesh->SetID(LoadedObjectID); + Meshes.erase(OldID); + Meshes[NewMesh->GetObjectID()] = NewMesh; } - delete[] ObjectID; - delete[] Buffer; delete[] VertexBuffer; delete[] TexBuffer; @@ -1101,9 +1151,7 @@ FEMesh* FEResourceManager::LoadFEMesh(const char* FileName, const std::string Na NewMesh->AABB = MeshAABB; NewMesh->SetName(Name); - - Meshes.erase(OldID); - Meshes[NewMesh->GetObjectID()] = NewMesh; + NewMesh->Tag = ObjectData.Tag; return NewMesh; } @@ -1117,85 +1165,139 @@ FEMaterial* FEResourceManager::CreateMaterial(std::string Name, const std::strin if (!ForceObjectID.empty()) NewMaterial->SetID(ForceObjectID); Materials[NewMaterial->GetObjectID()] = NewMaterial; - + return Materials[NewMaterial->GetObjectID()]; } -FEEntity* FEResourceManager::CreateEntity(FEGameModel* GameModel, const std::string Name, const std::string ForceObjectID) +Json::Value FEResourceManager::SaveMaterialToJSON(FEMaterial* Material) { - if (GameModel == nullptr) - GameModel = StandardGameModels["67251E393508013ZV579315F"]; + Json::Value Root; - FEPrefab* TempPrefab = CreatePrefab(GameModel, GameModel->GetName()); - FEEntity* NewEntity = new FEEntity(TempPrefab, Name); - if (!ForceObjectID.empty()) - NewEntity->SetID(ForceObjectID); - return NewEntity; + for (size_t i = 0; i < FE_MAX_TEXTURES_PER_MATERIAL; i++) + { + if (Material->Textures[i] != nullptr) + Root["Textures"][std::to_string(i).c_str()] = Material->Textures[i]->GetObjectID(); + + if (Material->TextureBindings[i] != -1) + Root["Texture bindings"][std::to_string(i).c_str()] = Material->TextureBindings[i]; + + if (Material->TextureChannels[i] != -1) + Root["Texture channels"][std::to_string(i).c_str()] = Material->TextureChannels[i]; + } + + Root["FEObjectData"] = RESOURCE_MANAGER.SaveFEObjectPart(Material); + Root["Metalness"] = Material->GetMetalness(); + Root["Roughness"] = Material->GetRoughness(); + Root["NormalMap intensity"] = Material->GetNormalMapIntensity(); + Root["AmbientOcclusion intensity"] = Material->GetAmbientOcclusionIntensity(); + Root["AmbientOcclusionMap intensity"] = Material->GetAmbientOcclusionMapIntensity(); + Root["RoughnessMap intensity"] = Material->GetRoughnessMapIntensity(); + Root["MetalnessMap intensity"] = Material->GetMetalnessMapIntensity(); + Root["Tiling"] = Material->GetTiling(); + Root["Compack packing"] = Material->IsCompackPacking(); + + return Root; } -FEEntity* FEResourceManager::CreateEntity(FEPrefab* Prefab, const std::string Name, const std::string ForceObjectID) +FEMaterial* FEResourceManager::LoadMaterialFromJSON(Json::Value& Root) { - if (Prefab == nullptr) - Prefab = new FEPrefab(StandardGameModels["67251E393508013ZV579315F"], Name); + FEObjectLoadedData LoadedObjectData = RESOURCE_MANAGER.LoadFEObjectPart(Root["FEObjectData"]); - FEEntity* NewEntity = new FEEntity(Prefab, Name); - if (!ForceObjectID.empty()) - NewEntity->SetID(ForceObjectID); - return NewEntity; + FEMaterial* NewMaterial = RESOURCE_MANAGER.CreateMaterial(LoadedObjectData.Name, LoadedObjectData.ID); + RESOURCE_MANAGER.SetTag(NewMaterial, LoadedObjectData.Tag); + NewMaterial->Shader = RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/); + + std::vector MembersList = Root.getMemberNames(); + for (size_t i = 0; i < MembersList.size(); i++) + { + if (MembersList[i] == "Textures") + { + for (size_t j = 0; j < FE_MAX_TEXTURES_PER_MATERIAL; j++) + { + if (Root["Textures"].isMember(std::to_string(j).c_str())) + { + std::string TextureID = Root["Textures"][std::to_string(j).c_str()].asCString(); + NewMaterial->Textures[j] = RESOURCE_MANAGER.GetTexture(TextureID); + if (NewMaterial->Textures[j] == nullptr) + NewMaterial->Textures[j] = RESOURCE_MANAGER.NoTexture; + } + } + } + + if (MembersList[i] == "Texture bindings") + { + for (size_t j = 0; j < FE_MAX_TEXTURES_PER_MATERIAL; j++) + { + if (Root["Texture bindings"].isMember(std::to_string(j).c_str())) + { + int Binding = Root["Texture bindings"][std::to_string(j).c_str()].asInt(); + NewMaterial->TextureBindings[j] = Binding; + } + } + } + + if (MembersList[i] == "Texture channels") + { + for (size_t j = 0; j < FE_MAX_TEXTURES_PER_MATERIAL; j++) + { + if (Root["Texture channels"].isMember(std::to_string(j).c_str())) + { + int binding = Root["Texture channels"][std::to_string(j).c_str()].asInt(); + NewMaterial->TextureChannels[j] = binding; + } + } + } + } + + NewMaterial->SetMetalness(Root["Metalness"].asFloat()); + NewMaterial->SetRoughness(Root["Roughness"].asFloat()); + NewMaterial->SetNormalMapIntensity(Root["NormalMap intensity"].asFloat()); + NewMaterial->SetAmbientOcclusionIntensity(Root["AmbientOcclusion intensity"].asFloat()); + NewMaterial->SetAmbientOcclusionMapIntensity(Root["AmbientOcclusionMap intensity"].asFloat()); + NewMaterial->SetRoughnessMapIntensity(Root["RoughnessMap intensity"].asFloat()); + NewMaterial->SetMetalnessMapIntensity(Root["MetalnessMap intensity"].asFloat()); + + if (Root.isMember("Tiling")) + NewMaterial->SetTiling(Root["Tiling"].asFloat()); + NewMaterial->SetCompackPacking(Root["Compack packing"].asBool()); + + return NewMaterial; } -std::vector FEResourceManager::GetMaterialList() +std::vector FEResourceManager::GetMaterialIDList() { FE_MAP_TO_STR_VECTOR(Materials) } -std::vector FEResourceManager::GetStandardMaterialList() +std::vector FEResourceManager::GetEnginePrivateMaterialIDList() { - FE_MAP_TO_STR_VECTOR(StandardMaterials) + return GetResourceIDListByTag(Materials, ENGINE_RESOURCE_TAG); } FEMaterial* FEResourceManager::GetMaterial(const std::string ID) { if (Materials.find(ID) == Materials.end()) - { - if (StandardMaterials.find(ID) != StandardMaterials.end()) - { - return StandardMaterials[ID]; - } - return nullptr; - } return Materials[ID]; } std::vector FEResourceManager::GetMaterialByName(const std::string Name) { - std::vector result; + std::vector Result; auto it = Materials.begin(); while (it != Materials.end()) { if (it->second->GetName() == Name) { - result.push_back(it->second); - } - - it++; - } - - it = StandardMaterials.begin(); - while (it != StandardMaterials.end()) - { - if (it->second->GetName() == Name) - { - result.push_back(it->second); + Result.push_back(it->second); } it++; } - return result; + return Result; } std::string FEResourceManager::GetFileNameFromFilePath(const std::string FilePath) @@ -1209,351 +1311,194 @@ std::string FEResourceManager::GetFileNameFromFilePath(const std::string FilePat return std::string(""); } -std::vector FEResourceManager::GetMeshList() +std::vector FEResourceManager::GetMeshIDList() { FE_MAP_TO_STR_VECTOR(Meshes) } -std::vector FEResourceManager::GetStandardMeshList() +std::vector FEResourceManager::GetEnginePrivateMeshIDList() { - FE_MAP_TO_STR_VECTOR(StandardMeshes) + return GetResourceIDListByTag(Meshes, ENGINE_RESOURCE_TAG); } FEMesh* FEResourceManager::GetMesh(const std::string ID) { if (Meshes.find(ID) == Meshes.end()) - { - if (StandardMeshes.find(ID) != StandardMeshes.end()) - { - return StandardMeshes[ID]; - } - return nullptr; - } - else - { - return Meshes[ID]; - } -} - -std::vector FEResourceManager::GetMeshByName(const std::string Name) -{ - std::vector result; - - auto it = Meshes.begin(); - while (it != Meshes.end()) - { - if (it->second->GetName() == Name) - { - result.push_back(it->second); - } - - it++; - } - - it = StandardMeshes.begin(); - while (it != StandardMeshes.end()) - { - if (it->second->GetName() == Name) - { - result.push_back(it->second); - } - - it++; - } - return result; + return Meshes[ID]; } -bool FEResourceManager::MakeMaterialStandard(FEMaterial* Material) +std::vector FEResourceManager::GetMeshByName(const std::string Name) { - if (Material == nullptr) + std::vector Result; + auto MeshIterator = Meshes.begin(); + while (MeshIterator != Meshes.end()) { - LOG.Add("material is nullptr in function FEResourceManager::makeMaterialStandard.", "FE_LOG_GENERAL", FE_LOG_ERROR); - return false; - } - - if (StandardMaterials.find(Material->GetObjectID()) == StandardMaterials.end()) - { - if (Materials.find(Material->GetObjectID()) != Materials.end()) - Materials.erase(Material->GetObjectID()); - StandardMaterials[Material->GetObjectID()] = Material; + if (MeshIterator->second->GetName() == Name) + Result.push_back(MeshIterator->second); - return true; + MeshIterator++; } - return false; + return Result; } void FEResourceManager::LoadStandardMaterial() { - FEMaterial* NewMaterial = CreateMaterial("SolidColorMaterial"); - Materials.erase(NewMaterial->GetObjectID()); - NewMaterial->SetID("18251A5E0F08013Z3939317U"/*"SolidColorMaterial"*/); + FEMaterial* NewMaterial = CreateMaterial("SolidColorMaterial", "18251A5E0F08013Z3939317U"); + NewMaterial->SetTag(ENGINE_RESOURCE_TAG); NewMaterial->Shader = CreateShader("FESolidColorShader", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//SolidColorMaterial//FE_SolidColor_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//SolidColorMaterial//FE_SolidColor_FS.glsl").c_str()).c_str()); - NewMaterial->Shader->SetID("6917497A5E0C05454876186F"); + LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//SolidColorMaterial//FE_SolidColor_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "6917497A5E0C05454876186F"); + NewMaterial->Shader->SetTag(ENGINE_RESOURCE_TAG); - MakeShaderStandard(NewMaterial->Shader); - const FEShaderParam color(glm::vec3(1.0f, 0.4f, 0.6f), "baseColor"); - NewMaterial->AddParameter(color); - MakeMaterialStandard(NewMaterial); + FEShaderUniformValue Color("baseColor", glm::vec3(1.0f, 0.4f, 0.6f)); + NewMaterial->SetUniformVariation(Color); + NewMaterial->SetTag(ENGINE_RESOURCE_TAG); FEShader* FEPhongShader = CreateShader("FEPhongShader", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PhongMaterial//FE_Phong_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PhongMaterial//FE_Phong_FS.glsl").c_str()).c_str()); - Shaders.erase(FEPhongShader->GetObjectID()); - FEPhongShader->SetID("4C41665B5E125C2A07456E44"/*"FEPhongShader"*/); - Shaders[FEPhongShader->GetObjectID()] = FEPhongShader; - - MakeShaderStandard(GetShader("4C41665B5E125C2A07456E44"/*"FEPhongShader"*/)); + LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PhongMaterial//FE_Phong_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "4C41665B5E125C2A07456E44"); + FEPhongShader->SetTag(ENGINE_RESOURCE_TAG); // ****************************** PBR SHADER ****************************** FEShader* PBRShader = CreateShader("FEPBRShader", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_VS_GBUFFER.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_FS_DEFERRED.glsl").c_str()).c_str()); + LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_FS_DEFERRED.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "0800253C242B05321A332D09"); - FEShader* PBRShaderForward = CreateShader("FEPBRShaderForward", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_FS.glsl").c_str()).c_str()); + PBRShader->SetTag(ENGINE_RESOURCE_TAG); - Shaders.erase(PBRShaderForward->GetObjectID()); - PBRShaderForward->SetID("5E45017E664A62273E191500"/*"FEPBRShaderForward"*/); - Shaders[PBRShaderForward->GetObjectID()] = PBRShaderForward; + FEShader* PBRShaderForward = CreateShader("FEPBRShaderForward", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_VS.glsl").c_str()).c_str(), + LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "5E45017E664A62273E191500"); - MakeShaderStandard(GetShader("5E45017E664A62273E191500"/*"FEPBRShaderForward"*/)); + PBRShaderForward->SetTag(ENGINE_RESOURCE_TAG); FEShader* PBRGBufferShader = CreateShader("FEPBRGBufferShader", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_FS_GBUFFER.glsl").c_str()).c_str()); + LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_FS_GBUFFER.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "670B01496E202658377A4576"); - Shaders.erase(PBRGBufferShader->GetObjectID()); - PBRGBufferShader->SetID("670B01496E202658377A4576"/*"FEPBRGBufferShader"*/); - Shaders[PBRGBufferShader->GetObjectID()] = PBRGBufferShader; + PBRGBufferShader->SetTag(ENGINE_RESOURCE_TAG); - MakeShaderStandard(GetShader("670B01496E202658377A4576"/*"FEPBRGBufferShader"*/)); - - Shaders.erase(PBRShader->GetObjectID()); - PBRShader->SetID("0800253C242B05321A332D09"/*"FEPBRShader"*/); - Shaders[PBRShader->GetObjectID()] = PBRShader; - - MakeShaderStandard(GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)); FEShader* PBRInstancedShader = CreateShader("FEPBRInstancedShader", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_INSTANCED_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_FS_DEFERRED.glsl").c_str()).c_str()); - - FEShader* PBRInstancedGBufferShader = CreateShader("FEPBRInstancedGBufferShader", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_INSTANCED_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_FS_GBUFFER.glsl").c_str()).c_str()); + LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_FS_DEFERRED.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "7C80085C184442155D0F3C7B"); - Shaders.erase(PBRInstancedGBufferShader->GetObjectID()); - PBRInstancedGBufferShader->SetID("613830232E12602D6A1D2C17"/*"FEPBRInstancedGBufferShader"*/); - Shaders[PBRInstancedGBufferShader->GetObjectID()] = PBRInstancedGBufferShader; + PBRInstancedShader->SetTag(ENGINE_RESOURCE_TAG); - MakeShaderStandard(GetShader("613830232E12602D6A1D2C17"/*"FEPBRInstancedGBufferShader"*/)); - - Shaders.erase(PBRInstancedShader->GetObjectID()); - PBRInstancedShader->SetID("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/); - Shaders[PBRInstancedShader->GetObjectID()] = PBRInstancedShader; + FEShader* PBRInstancedGBufferShader = CreateShader("FEPBRInstancedGBufferShader", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_INSTANCED_VS.glsl").c_str()).c_str(), + LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//PBRMaterial//FE_PBR_FS_GBUFFER.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "613830232E12602D6A1D2C17"); - MakeShaderStandard(GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)); + PBRInstancedGBufferShader->SetTag(ENGINE_RESOURCE_TAG); - NewMaterial = CreateMaterial("FEPBRBaseMaterial"); - Materials.erase(NewMaterial->GetObjectID()); - NewMaterial->SetID("61649B9E0F08013Q3939316C"/*"FEPBRBaseMaterial"*/); + NewMaterial = CreateMaterial("FEPBRBaseMaterial", "61649B9E0F08013Q3939316C" /*"FEPBRBaseMaterial"*/); + NewMaterial->SetTag(ENGINE_RESOURCE_TAG); NewMaterial->Shader = GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/); NewMaterial->SetAlbedoMap(NoTexture); - MakeMaterialStandard(NewMaterial); + NewMaterial->SetTag(ENGINE_RESOURCE_TAG); // ****************************** PBR SHADER END ****************************** - FEShader* TerrainShader = CreateShader("FETerrainShader", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//FE_Terrain_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//FE_Terrain_FS_GBUFFER.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//FE_Terrain_TCS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//FE_Terrain_TES.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//FE_Terrain_GS.glsl").c_str()).c_str()); - - // ****************************** TERRAIN ****************************** - Shaders.erase(TerrainShader->GetObjectID()); - TerrainShader->SetID("5A3E4F5C13115856401F1D1C"/*"FETerrainShader"*/); - Shaders[TerrainShader->GetObjectID()] = TerrainShader; - - MakeShaderStandard(GetShader("5A3E4F5C13115856401F1D1C"/*"FETerrainShader"*/)); - - FEShader* ShadowMapTerrainShader = CreateShader("FESMTerrainShader", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//ShadowMapShader//FE_SMTerrain_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//ShadowMapShader//FE_SMTerrain_FS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//ShadowMapShader//FE_SMTerrain_TCS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//ShadowMapShader//FE_SMTerrain_TES.glsl").c_str()).c_str()); - Shaders.erase(ShadowMapTerrainShader->GetObjectID()); - ShadowMapTerrainShader->SetID("50064D3C4D0B537F0846274F"/*"FESMTerrainShader"*/); - Shaders[ShadowMapTerrainShader->GetObjectID()] = ShadowMapTerrainShader; - - const FEShaderParam ColorParam(glm::vec3(1.0f, 1.0f, 1.0f), "baseColor"); - GetShader("50064D3C4D0B537F0846274F"/*"FESMTerrainShader"*/)->AddParameter(ColorParam); - - MakeShaderStandard(GetShader("50064D3C4D0B537F0846274F"/*"FESMTerrainShader"*/)); - - FEShader* TerrainBrushOutput = CreateShader("terrainBrushOutput", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_BrushOutput_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_BrushOutput_FS.glsl").c_str()).c_str()); - Shaders.erase(TerrainBrushOutput->GetObjectID()); - TerrainBrushOutput->SetID("49654A4A10604C2A1221426B"/*"terrainBrushOutput"*/); - Shaders[TerrainBrushOutput->GetObjectID()] = TerrainBrushOutput; - MakeShaderStandard(GetShader("49654A4A10604C2A1221426B"/*"terrainBrushOutput"*/)); - - FEShader* TerrainBrushVisual = CreateShader("terrainBrushVisual", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_BrushVisual_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_BrushVisual_FS.glsl").c_str()).c_str()); - Shaders.erase(TerrainBrushVisual->GetObjectID()); - TerrainBrushVisual->SetID("40064B7B4287805B296E526E"/*"terrainBrushVisual"*/); - Shaders[TerrainBrushVisual->GetObjectID()] = TerrainBrushVisual; - MakeShaderStandard(GetShader("40064B7B4287805B296E526E"/*"terrainBrushVisual"*/)); - - FEShader* TerrainLayersNormalize = CreateShader("terrainLayersNormalize", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_BrushOutput_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_LayersNormalize_FS.glsl").c_str()).c_str()); - Shaders.erase(TerrainLayersNormalize->GetObjectID()); - TerrainLayersNormalize->SetID("19294C00394A346A576F401C"/*"terrainLayersNormalize"*/); - Shaders[TerrainLayersNormalize->GetObjectID()] = TerrainLayersNormalize; - MakeShaderStandard(GetShader("19294C00394A346A576F401C"/*"terrainLayersNormalize"*/)); - // ****************************** TERRAIN END ****************************** - - FEMaterial* SkyDomeMaterial = CreateMaterial("skyDomeMaterial"); - Materials.erase(SkyDomeMaterial->GetObjectID()); - SkyDomeMaterial->SetID("5A649B9E0F36073D4939313H"); - SkyDomeMaterial->Shader = CreateShader("FESkyDome", LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//SkyDome//FE_SkyDome_VS.glsl").c_str()).c_str(), - LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//SkyDome//FE_SkyDome_FS.glsl").c_str()).c_str()); - Shaders.erase(SkyDomeMaterial->Shader->GetObjectID()); - SkyDomeMaterial->Shader->SetID("3A69744E831A574E4857361B"); - Shaders[SkyDomeMaterial->Shader->GetObjectID()] = SkyDomeMaterial->Shader; - - MakeShaderStandard(SkyDomeMaterial->Shader); - MakeMaterialStandard(SkyDomeMaterial); - // same as FERenderer::updateFogInShaders() - GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateParameterData("fogDensity", 0.007f); - GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateParameterData("fogGradient", 2.5f); - GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateParameterData("shadowBlurFactor", 1.0f); + GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateUniformData("fogDensity", 0.007f); + GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateUniformData("fogGradient", 2.5f); + GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/)->UpdateUniformData("shadowBlurFactor", 1.0f); - GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateParameterData("fogDensity", 0.007f); - GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateParameterData("fogGradient", 2.5f); - GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateParameterData("shadowBlurFactor", 1.0f); + GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateUniformData("fogDensity", 0.007f); + GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateUniformData("fogGradient", 2.5f); + GetShader("7C80085C184442155D0F3C7B"/*"FEPBRInstancedShader"*/)->UpdateUniformData("shadowBlurFactor", 1.0f); } void FEResourceManager::LoadStandardGameModels() { FEGameModel* NewGameModel = new FEGameModel(GetMesh("7F251E3E0D08013E3579315F"/*"sphere"*/), GetMaterial("18251A5E0F08013Z3939317U"/*"SolidColorMaterial"*/), "standardGameModel"); + GameModels.erase(NewGameModel->GetObjectID()); NewGameModel->SetID("67251E393508013ZV579315F"); - MakeGameModelStandard(NewGameModel); - - NewGameModel = new FEGameModel(GetMesh("7F251E3E0D08013E3579315F"), GetMaterial("5A649B9E0F36073D4939313H"/*"skyDomeMaterial"*/), "skyDomeGameModel"); - NewGameModel->SetID("17271E603508013IO77931TY"); - MakeGameModelStandard(NewGameModel); + NewGameModel->SetTag(ENGINE_RESOURCE_TAG); + GameModels[NewGameModel->GetObjectID()] = NewGameModel; } void FEResourceManager::Clear() { - auto MaterialIt = Materials.begin(); - while (MaterialIt != Materials.end()) - { - delete MaterialIt->second; - MaterialIt++; - } - Materials.clear(); - - auto MeshIt = Meshes.begin(); - while (MeshIt != Meshes.end()) - { - delete MeshIt->second; - MeshIt++; - } - Meshes.clear(); - - auto TextureIt = Textures.begin(); - while (TextureIt != Textures.end()) - { - delete TextureIt->second; - TextureIt++; - } - Textures.clear(); - - auto GameModelIt = GameModels.begin(); - while (GameModelIt != GameModels.end()) - { - delete GameModelIt->second; - GameModelIt++; - } - GameModels.clear(); - - auto PrefabIt = Prefabs.begin(); - while (PrefabIt != Prefabs.end()) - { - delete PrefabIt->second; - PrefabIt++; - } - Prefabs.clear(); + ClearResource(Materials); + ClearResource(Meshes); + ClearResource(Textures); + ClearResource(GameModels); + ClearResource(Prefabs); } void FEResourceManager::SaveFEMesh(FEMesh* Mesh, const char* FileName) { - std::fstream file; - file.open(FileName, std::ios::out | std::ios::binary); - - // Version of FEMesh file type. - float version = FE_MESH_VERSION; - file.write((char*)&version, sizeof(float)); + std::fstream File; + File.open(FileName, std::ios::out | std::ios::binary); - int ObjectIDSize = static_cast(Mesh->GetObjectID().size()); - file.write((char*)&ObjectIDSize, sizeof(int)); - file.write((char*)Mesh->GetObjectID().c_str(), sizeof(char) * ObjectIDSize); + // Version of FEMesh File type. + float Version = FE_MESH_VERSION; + File.write((char*)&Version, sizeof(float)); - int NameSize = static_cast(Mesh->GetName().size()); - file.write((char*)&NameSize, sizeof(int)); - file.write((char*)Mesh->GetName().c_str(), sizeof(char) * NameSize); + OBJECT_MANAGER.SaveFEObjectPart(File, Mesh); int Count = Mesh->GetPositionsCount(); float* Positions = new float[Count]; FE_GL_ERROR(glGetNamedBufferSubData(Mesh->GetPositionsBufferID(), 0, sizeof(float) * Count, Positions)); - file.write((char*)&Count, sizeof(int)); - file.write((char*)Positions, sizeof(float) * Count); + File.write((char*)&Count, sizeof(int)); + File.write((char*)Positions, sizeof(float) * Count); Count = Mesh->GetUVCount(); float* UV = new float[Count]; FE_GL_ERROR(glGetNamedBufferSubData(Mesh->GetUVBufferID(), 0, sizeof(float) * Count, UV)); - file.write((char*)&Count, sizeof(int)); - file.write((char*)UV, sizeof(float) * Count); + File.write((char*)&Count, sizeof(int)); + File.write((char*)UV, sizeof(float) * Count); Count = Mesh->GetNormalsCount(); float* Normals = new float[Count]; FE_GL_ERROR(glGetNamedBufferSubData(Mesh->GetNormalsBufferID(), 0, sizeof(float) * Count, Normals)); - file.write((char*)&Count, sizeof(int)); - file.write((char*)Normals, sizeof(float) * Count); - + File.write((char*)&Count, sizeof(int)); + File.write((char*)Normals, sizeof(float) * Count); + Count = Mesh->GetTangentsCount(); float* Tangents = new float[Count]; FE_GL_ERROR(glGetNamedBufferSubData(Mesh->GetTangentsBufferID(), 0, sizeof(float) * Count, Tangents)); - file.write((char*)&Count, sizeof(int)); - file.write((char*)Tangents, sizeof(float) * Count); + File.write((char*)&Count, sizeof(int)); + File.write((char*)Tangents, sizeof(float) * Count); Count = Mesh->GetIndicesCount(); int* Indices = new int[Count]; FE_GL_ERROR(glGetNamedBufferSubData(Mesh->GetIndicesBufferID(), 0, sizeof(int) * Count, Indices)); - file.write((char*)&Count, sizeof(int)); - file.write((char*)Indices, sizeof(int) * Count); + File.write((char*)&Count, sizeof(int)); + File.write((char*)Indices, sizeof(int) * Count); int MaterialCount = Mesh->MaterialsCount; - file.write((char*)&MaterialCount, sizeof(int)); - + File.write((char*)&MaterialCount, sizeof(int)); + if (MaterialCount > 1) { Count = Mesh->GetMaterialsIndicesCount(); float* MatIndices = new float[Count]; FE_GL_ERROR(glGetNamedBufferSubData(Mesh->GetMaterialsIndicesBufferID(), 0, sizeof(float) * Count, MatIndices)); - file.write((char*)&Count, sizeof(int)); - file.write((char*)MatIndices, sizeof(float) * Count); + File.write((char*)&Count, sizeof(int)); + File.write((char*)MatIndices, sizeof(float) * Count); } FEAABB TempAABB(Positions, Mesh->GetPositionsCount()); - file.write((char*)&TempAABB.Min[0], sizeof(float)); - file.write((char*)&TempAABB.Min[1], sizeof(float)); - file.write((char*)&TempAABB.Min[2], sizeof(float)); + File.write((char*)&TempAABB.Min[0], sizeof(float)); + File.write((char*)&TempAABB.Min[1], sizeof(float)); + File.write((char*)&TempAABB.Min[2], sizeof(float)); - file.write((char*)&TempAABB.Max[0], sizeof(float)); - file.write((char*)&TempAABB.Max[1], sizeof(float)); - file.write((char*)&TempAABB.Max[2], sizeof(float)); + File.write((char*)&TempAABB.Max[0], sizeof(float)); + File.write((char*)&TempAABB.Max[1], sizeof(float)); + File.write((char*)&TempAABB.Max[2], sizeof(float)); - file.close(); + File.close(); delete[] Positions; delete[] UV; @@ -1562,7 +1507,7 @@ void FEResourceManager::SaveFEMesh(FEMesh* Mesh, const char* FileName) delete[] Indices; } -std::vector FEResourceManager::GetTextureList() +std::vector FEResourceManager::GetTextureIDList() { FE_MAP_TO_STR_VECTOR(Textures) } @@ -1577,20 +1522,20 @@ FETexture* FEResourceManager::GetTexture(const std::string ID) std::vector FEResourceManager::GetTextureByName(const std::string Name) { - std::vector result; + std::vector Result; - auto it = Textures.begin(); - while (it != Textures.end()) + auto TextureIterator = Textures.begin(); + while (TextureIterator != Textures.end()) { - if (it->second->GetName() == Name) + if (TextureIterator->second->GetName() == Name) { - result.push_back(it->second); + Result.push_back(TextureIterator->second); } - it++; + TextureIterator++; } - return result; + return Result; } void FEResourceManager::DeleteFETexture(const FETexture* Texture) @@ -1618,7 +1563,7 @@ void FEResourceManager::DeleteFETexture(const FETexture* Texture) MaterialIterator++; } - // After we make sure that texture is no more referenced by any material, we can delete it. + // After we make sure that texture is no more referenced by any material, we can delete TextureIterator. Textures.erase(Texture->GetObjectID()); delete Texture; @@ -1643,27 +1588,20 @@ void FEResourceManager::DeleteFEMesh(const FEMesh* Mesh) delete Mesh; } -std::vector FEResourceManager::GetGameModelList() +std::vector FEResourceManager::GetGameModelIDList() { FE_MAP_TO_STR_VECTOR(GameModels) } -std::vector FEResourceManager::GetStandardGameModelList() +std::vector FEResourceManager::GetEnginePrivateGameModelIDList() { - FE_MAP_TO_STR_VECTOR(StandardGameModels) + return GetResourceIDListByTag(GameModels, ENGINE_RESOURCE_TAG); } FEGameModel* FEResourceManager::GetGameModel(const std::string ID) { if (GameModels.find(ID) == GameModels.end()) - { - if (StandardGameModels.find(ID) != StandardGameModels.end()) - { - return StandardGameModels[ID]; - } - return nullptr; - } return GameModels[ID]; } @@ -1672,26 +1610,15 @@ std::vector FEResourceManager::GetGameModelByName(const std::strin { std::vector Result; - auto it = GameModels.begin(); - while (it != GameModels.end()) - { - if (it->second->GetName() == Name) - { - Result.push_back(it->second); - } - - it++; - } - - it = StandardGameModels.begin(); - while (it != StandardGameModels.end()) + auto GameModelIterator = GameModels.begin(); + while (GameModelIterator != GameModels.end()) { - if (it->second->GetName() == Name) + if (GameModelIterator->second->GetName() == Name) { - Result.push_back(it->second); + Result.push_back(GameModelIterator->second); } - it++; + GameModelIterator++; } return Result; @@ -1718,48 +1645,97 @@ FEGameModel* FEResourceManager::CreateGameModel(FEMesh* Mesh, FEMaterial* Materi { GameModels[NewGameModel->ID] = NewGameModel; } - + GameModels[NewGameModel->ID]->SetName(Name); return GameModels[NewGameModel->ID]; } -void FEResourceManager::DeleteGameModel(const FEGameModel* GameModel) +Json::Value FEResourceManager::SaveGameModelToJSON(FEGameModel* GameModel) { - GameModels.erase(GameModel->GetObjectID()); - delete GameModel; + Json::Value Root; + + Root["FEObjectData"] = RESOURCE_MANAGER.SaveFEObjectPart(GameModel); + Root["Mesh"] = GameModel->Mesh->GetObjectID(); + Root["Material"] = GameModel->Material->GetObjectID(); + Root["ScaleFactor"] = GameModel->GetScaleFactor(); + + Root["LODs"]["HaveLODlevels"] = GameModel->IsUsingLOD(); + if (GameModel->IsUsingLOD()) + { + Root["LODs"]["CullDistance"] = GameModel->GetCullDistance(); + Root["LODs"]["Billboard zero rotaion"] = GameModel->GetBillboardZeroRotaion(); + Root["LODs"]["LODCount"] = GameModel->GetLODCount(); + for (size_t i = 0; i < GameModel->GetLODCount(); i++) + { + Root["LODs"][std::to_string(i)]["Mesh"] = GameModel->GetLODMesh(i)->GetObjectID(); + Root["LODs"][std::to_string(i)]["Max draw distance"] = GameModel->GetLODMaxDrawDistance(i); + Root["LODs"][std::to_string(i)]["IsBillboard"] = GameModel->IsLODBillboard(i); + if (GameModel->IsLODBillboard(i)) + Root["LODs"][std::to_string(i)]["Billboard material"] = GameModel->GetBillboardMaterial()->GetObjectID(); + } + } + + return Root; } -bool FEResourceManager::MakeGameModelStandard(FEGameModel* GameModel) +FEGameModel* FEResourceManager::LoadGameModelFromJSON(Json::Value& Root) { - if (GameModel == nullptr) + FEObjectLoadedData LoadedObjectData = RESOURCE_MANAGER.LoadFEObjectPart(Root["FEObjectData"]); + + FEGameModel* NewGameModel = RESOURCE_MANAGER.CreateGameModel(RESOURCE_MANAGER.GetMesh(Root["Mesh"].asCString()), + RESOURCE_MANAGER.GetMaterial(Root["Material"].asCString()), + LoadedObjectData.Name, LoadedObjectData.ID); + + if (NewGameModel == nullptr) { - LOG.Add("gameModel is nullptr in function FEResourceManager::makeGameModelStandard.", "FE_LOG_GENERAL", FE_LOG_ERROR); - return false; + LOG.Add("Can't load game model from JSON in function FEResourceManager::LoadGameModelFromJSON.", "FE_LOG_LOADING", FE_LOG_ERROR); + return nullptr; } - if (StandardGameModels.find(GameModel->GetObjectID()) == StandardGameModels.end()) + RESOURCE_MANAGER.SetTag(NewGameModel, LoadedObjectData.Tag); + + NewGameModel->SetScaleFactor(Root["ScaleFactor"].asFloat()); + + bool bHaveLODLevels = Root["LODs"]["HaveLODlevels"].asBool(); + NewGameModel->SetUsingLOD(bHaveLODLevels); + if (bHaveLODLevels) { - if (GameModels.find(GameModel->GetObjectID()) != GameModels.end()) - GameModels.erase(GameModel->GetObjectID()); - StandardGameModels[GameModel->GetObjectID()] = GameModel; + NewGameModel->SetCullDistance(Root["LODs"]["CullDistance"].asFloat()); + NewGameModel->SetBillboardZeroRotaion(Root["LODs"]["Billboard zero rotaion"].asFloat()); + + size_t LODCount = Root["LODs"]["LODCount"].asInt(); + for (size_t i = 0; i < LODCount; i++) + { + NewGameModel->SetLODMesh(i, RESOURCE_MANAGER.GetMesh(Root["LODs"][std::to_string(i)]["Mesh"].asString())); + NewGameModel->SetLODMaxDrawDistance(i, Root["LODs"][std::to_string(i)]["Max draw distance"].asFloat()); - return true; + bool bLODBillboard = Root["LODs"][std::to_string(i)]["IsBillboard"].asBool(); + NewGameModel->SetIsLODBillboard(i, bLODBillboard); + if (bLODBillboard) + NewGameModel->SetBillboardMaterial(RESOURCE_MANAGER.GetMaterial(Root["LODs"][std::to_string(i)]["Billboard material"].asString())); + } } - return false; + return NewGameModel; +} + +void FEResourceManager::DeleteGameModel(const FEGameModel* GameModel) +{ + GameModels.erase(GameModel->GetObjectID()); + delete GameModel; } FEShader* FEResourceManager::CreateShader(std::string ShaderName, const char* VertexText, const char* FragmentText, - const char* TessControlText, const char* TessEvalText, - const char* GeometryText, const char* ComputeText, const std::string ForceObjectID) + const char* TessControlText, const char* TessEvalText, + const char* GeometryText, const char* ComputeText, const std::string ForceObjectID) { if (ShaderName.empty()) ShaderName = "unnamedShader"; // Shader with compute stage cannot contain any other stage. if (ComputeText != nullptr && (VertexText != nullptr || FragmentText != nullptr || - TessControlText != nullptr || TessEvalText != nullptr || - GeometryText != nullptr)) + TessControlText != nullptr || TessEvalText != nullptr || + GeometryText != nullptr)) { return nullptr; } @@ -1771,80 +1747,40 @@ FEShader* FEResourceManager::CreateShader(std::string ShaderName, const char* Ve return NewShader; } -bool FEResourceManager::MakeShaderStandard(FEShader* Shader) -{ - if (Shader == nullptr) - { - LOG.Add("shader is nullptr in function FEResourceManager::makeShaderStandard.", "FE_LOG_GENERAL", FE_LOG_ERROR); - return false; - } - - if (StandardShaders.find(Shader->GetObjectID()) == StandardShaders.end()) - { - if (Shaders.find(Shader->GetObjectID()) != Shaders.end()) - Shaders.erase(Shader->GetObjectID()); - StandardShaders[Shader->GetObjectID()] = Shader; - - return true; - } - - return false; -} - FEShader* FEResourceManager::GetShader(const std::string ShaderID) { if (Shaders.find(ShaderID) == Shaders.end()) - { - if (StandardShaders.find(ShaderID) != StandardShaders.end()) - { - return StandardShaders[ShaderID]; - } - return nullptr; - } - else - { - return Shaders[ShaderID]; - } + + return Shaders[ShaderID]; } std::vector FEResourceManager::GetShaderByName(const std::string Name) { std::vector Result; - auto it = Shaders.begin(); - while (it != Shaders.end()) - { - if (it->second->GetName() == Name) - { - Result.push_back(it->second); - } - - it++; - } - - it = StandardShaders.begin(); - while (it != StandardShaders.end()) + auto ShaderIterator = Shaders.begin(); + while (ShaderIterator != Shaders.end()) { - if (it->second->GetName() == Name) + if (ShaderIterator->second->GetName() == Name) { - Result.push_back(it->second); + Result.push_back(ShaderIterator->second); } - it++; + ShaderIterator++; } return Result; } -std::vector FEResourceManager::GetShadersList() +std::vector FEResourceManager::GetShaderIDList() { FE_MAP_TO_STR_VECTOR(Shaders) } -std::vector FEResourceManager::GetStandardShadersList() +std::vector FEResourceManager::GetEnginePrivateShaderIDList() { - FE_MAP_TO_STR_VECTOR(StandardShaders) + return GetResourceIDListByTag(Shaders, ENGINE_RESOURCE_TAG); } void FEResourceManager::DeleteShader(const FEShader* Shader) @@ -1852,26 +1788,22 @@ void FEResourceManager::DeleteShader(const FEShader* Shader) if (Shader == nullptr) return; - auto it = Materials.begin(); - while (it != Materials.end()) + if (Shader->GetTag() == ENGINE_RESOURCE_TAG) { - if (it->second->Shader->GetNameHash() == Shader->GetNameHash()) - it->second->Shader = GetShader("6917497A5E0C05454876186F"/*"FESolidColorShader"*/); - - it++; + LOG.Add("Attempt to delete engine private shader in function FEResourceManager::DeleteShader.", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; } - it = StandardMaterials.begin(); - while (it != StandardMaterials.end()) + auto MaterialsIterator = Materials.begin(); + while (MaterialsIterator != Materials.end()) { - if (it->second->Shader->GetNameHash() == Shader->GetNameHash()) - it->second->Shader = GetShader("6917497A5E0C05454876186F"/*"FESolidColorShader"*/); + if (MaterialsIterator->second->Shader->GetNameHash() == Shader->GetNameHash()) + MaterialsIterator->second->Shader = GetShader("6917497A5E0C05454876186F"/*"FESolidColorShader"*/); - it++; + MaterialsIterator++; } Shaders.erase(Shader->GetObjectID()); - StandardShaders.erase(Shader->GetObjectID()); delete Shader; } @@ -1886,201 +1818,43 @@ bool FEResourceManager::ReplaceShader(const std::string OldShaderID, FEShader* N if (Shaders.find(OldShaderID) != Shaders.end()) { - //*(shaders[oldShaderID]) = *newShader; - //newShader->setID(oldShaderID); Shaders[OldShaderID]->ReCompile(NewShader->GetName(), NewShader->GetVertexShaderText(), NewShader->GetFragmentShaderText(), - NewShader->GetTessControlShaderText(), NewShader->GetTessEvalShaderText(), - NewShader->GetGeometryShaderText(), NewShader->GetComputeShaderText()); - } - else if (StandardShaders.find(OldShaderID) != StandardShaders.end()) - { - //*(standardShaders[oldShaderID]) = *newShader; - //newShader->setID(oldShaderID); - - StandardShaders[OldShaderID]->ReCompile(NewShader->GetName(), NewShader->GetVertexShaderText(), NewShader->GetFragmentShaderText(), - NewShader->GetTessControlShaderText(), NewShader->GetTessEvalShaderText(), - NewShader->GetGeometryShaderText(), NewShader->GetComputeShaderText()); + NewShader->GetTessControlShaderText(), NewShader->GetTessEvalShaderText(), + NewShader->GetGeometryShaderText(), NewShader->GetComputeShaderText()); } return true; } -FETerrain* FEResourceManager::CreateTerrain(const bool bCreateHeightMap, const std::string Name, const std::string ForceObjectID) +// TO-DO: That function should be in TERRAIN_SYSTEM and FEResourceManager should just exepct general settings to create texture. +FETexture* FEResourceManager::CreateBlankHightMapTexture(int Width, int Height, std::string Name) { - FETerrain* NewTerrain = new FETerrain(Name); - if (!ForceObjectID.empty()) - NewTerrain->SetID(ForceObjectID); + if (Name.empty()) + Name = "UnnamedHeightMap"; + + FETexture* NewTexture = CreateTexture(Name); + NewTexture->Width = Width; + NewTexture->Height = Height; + NewTexture->InternalFormat = GL_R16; + NewTexture->MagFilter = FE_LINEAR; + NewTexture->FileName = "NULL"; - NewTerrain->Shader = GetShader("5A3E4F5C13115856401F1D1C"/*"FETerrainShader"*/); + FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); - if (bCreateHeightMap) + unsigned short* RawPixels = new unsigned short[NewTexture->Width * NewTexture->Height]; + for (size_t i = 0; i < static_cast(NewTexture->Width * NewTexture->Height); i++) { - //creating blank heightMap - FETexture* NewTexture = CreateTexture(Name + "_heightMap"); - NewTexture->Width = FE_TERRAIN_STANDARD_HIGHT_MAP_RESOLUTION; - NewTexture->Height = FE_TERRAIN_STANDARD_HIGHT_MAP_RESOLUTION; - NewTexture->InternalFormat = GL_R16; - NewTexture->MagFilter = FE_LINEAR; - NewTexture->FileName = "NULL"; + RawPixels[i] = static_cast(0xffff * 0.5); + } - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); + FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, NewTexture->InternalFormat, NewTexture->Width, NewTexture->Height, 0, GL_RED, GL_UNSIGNED_SHORT, (unsigned char*)RawPixels); + delete[] RawPixels; - unsigned short* RawPixels = new unsigned short[NewTexture->Width * NewTexture->Height]; - for (size_t i = 0; i < static_cast(NewTexture->Width * NewTexture->Height); i++) - { - RawPixels[i] = static_cast(0xffff * 0.5); - } + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, NewTexture->InternalFormat, NewTexture->Width, NewTexture->Height, 0, GL_RED, GL_UNSIGNED_SHORT, (unsigned char*)RawPixels); - delete[] RawPixels; - - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - - NewTerrain->HeightMapArray.resize(NewTexture->Width * NewTexture->Height); - for (size_t i = 0; i < NewTerrain->HeightMapArray.size(); i++) - { - NewTerrain->HeightMapArray[i] = 0.5f; - } - - const glm::vec3 MinPoint = glm::vec3(-1.0f, 0.5f, -1.0f); - const glm::vec3 MaxPoint = glm::vec3(1.0f, 0.5f, 1.0f); - NewTerrain->AABB = FEAABB(MinPoint, MaxPoint); - NewTerrain->HeightMap = NewTexture; - - InitTerrainEditTools(NewTerrain); - } - - return NewTerrain; -} - -void FEResourceManager::InitTerrainEditTools(FETerrain* Terrain) -{ - if (Terrain == nullptr) - { - LOG.Add("called FEResourceManager::initTerrainEditTools with nullptr terrain", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - if (Terrain->BrushOutputFB != nullptr) - { - delete Terrain->BrushOutputFB; - Terrain->BrushOutputFB = nullptr; - } - - if (Terrain->BrushVisualFB != nullptr) - { - delete Terrain->BrushVisualFB; - Terrain->BrushVisualFB = nullptr; - } - - Terrain->BrushOutputFB = CreateFramebuffer(FE_COLOR_ATTACHMENT, 32, 32); - delete Terrain->BrushOutputFB->GetColorAttachment(); - //terrain->brushOutputFB->setColorAttachment(new FETexture(GL_R16, GL_RED, terrain->heightMap->getWidth(), terrain->heightMap->getHeight())); - Terrain->BrushOutputFB->SetColorAttachment(Terrain->HeightMap); - - Terrain->BrushVisualFB = CreateFramebuffer(FE_COLOR_ATTACHMENT, Terrain->HeightMap->GetWidth(), Terrain->HeightMap->GetHeight()); - Terrain->ProjectedMap = Terrain->BrushVisualFB->GetColorAttachment(); - - Terrain->BrushOutputShader = GetShader("49654A4A10604C2A1221426B"/*"terrainBrushOutput"*/); - Terrain->LayersNormalizeShader = GetShader("19294C00394A346A576F401C"/*"terrainLayersNormalize"*/); - Terrain->BrushVisualShader = GetShader("40064B7B4287805B296E526E"/*"terrainBrushVisual"*/); - - Terrain->PlaneMesh = GetMesh("1Y251E6E6T78013635793156"/*"plane"*/); -} - -FETexture* FEResourceManager::LoadPNGHeightmap(const char* FileName, FETerrain* Terrain, std::string Name) -{ - FETexture* NewTexture = CreateTexture(Name); - - std::fstream file; - file.open(FileName, std::ios::in | std::ios::binary | std::ios::ate); - std::streamsize FileSize = file.tellg(); - file.seekg(0, std::ios::beg); - char* FileData = new char[static_cast(FileSize)]; - file.read(FileData, FileSize); - file.close(); - - unsigned UWidth, UHeight; - lodepng::State PNGState; - PNGState.info_raw.colortype = LCT_GREY; - PNGState.info_raw.bitdepth = 16; - std::vector RawData; - lodepng::decode(RawData, UWidth, UHeight, PNGState, (unsigned char*)FileData, static_cast(FileSize)); - - if (PNGState.info_png.color.colortype != LCT_GREY || PNGState.info_png.color.bitdepth != 16) - { - delete NewTexture; - LOG.Add(std::string("File: ") + FileName + " in function FEResourceManager::LoadPNGHeightmap is not 16 bit gray scale png.", "FE_LOG_LOADING", FE_LOG_ERROR); - return this->NoTexture; - } - - if (RawData.empty()) - { - delete NewTexture; - LOG.Add(std::string("can't read file: ") + FileName + " in function FEResourceManager::LoadPNGHeightmap.", "FE_LOG_LOADING", FE_LOG_ERROR); - return this->NoTexture; - } - - NewTexture->Width = UWidth; - NewTexture->Height = UHeight; - NewTexture->InternalFormat = GL_R16; - NewTexture->MagFilter = FE_LINEAR; - NewTexture->FileName = FileName; - - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); - // lodepng returns data with different bytes order that openGL expects. - FE_GL_ERROR(glPixelStorei(GL_UNPACK_SWAP_BYTES, TRUE)); - FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, NewTexture->InternalFormat, NewTexture->Width, NewTexture->Height, 0, GL_RED, GL_UNSIGNED_SHORT, RawData.data()); - FE_GL_ERROR(glPixelStorei(GL_UNPACK_SWAP_BYTES, FALSE)); - - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - - Terrain->HeightMapArray.resize(RawData.size() / sizeof(unsigned short)); - float Max = FLT_MIN; - float Min = FLT_MAX; - int Iterator = 0; - for (size_t i = 0; i < RawData.size(); i+=2) - { - unsigned short Temp = *(unsigned short*)(&RawData[i]); - Terrain->HeightMapArray[Iterator] = Temp / static_cast(0xFFFF); - - if (Max < Terrain->HeightMapArray[Iterator]) - Max = Terrain->HeightMapArray[Iterator]; - - if (Min > Terrain->HeightMapArray[Iterator]) - Min = Terrain->HeightMapArray[Iterator]; - - Iterator++; - } - - glm::vec3 MinPoint = glm::vec3(-1.0f, Min, -1.0f); - glm::vec3 MaxPoint = glm::vec3(1.0f, Max, 1.0f); - Terrain->AABB = FEAABB(MinPoint, MaxPoint); - - if (Name.empty()) - { - std::string FilePath = NewTexture->FileName; - std::size_t Index = FilePath.find_last_of("/\\"); - std::string NewFileName = FilePath.substr(Index + 1); - Index = NewFileName.find_last_of("."); - std::string FileNameWithOutExtention = NewFileName.substr(0, Index); - NewTexture->SetName(FileNameWithOutExtention); - } - - if (Terrain->HeightMap != nullptr) - DeleteFETexture(Terrain->HeightMap); - - Terrain->HeightMap = NewTexture; - InitTerrainEditTools(Terrain); - Terrain->UpdateCpuHeightInfo(); - - return NewTexture; -} + return NewTexture; +} std::string FEResourceManager::LoadGLSL(const char* FileName) { @@ -2099,7 +1873,7 @@ std::string FEResourceManager::LoadGLSL(const char* FileName) } else { - LOG.Add(std::string("can't load file: ") + FileName + " in function FEResourceManager::loadGLSL.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add(std::string("can't load file: ") + FileName + " in function FEResourceManager::LoadGLSL.", "FE_LOG_LOADING", FE_LOG_ERROR); } return ShaderData; @@ -2125,7 +1899,7 @@ FEFramebuffer* FEResourceManager::CreateFramebuffer(const int Attachments, const { NewFramebuffer->ColorAttachments[i] = nullptr; } - + NewFramebuffer->Width = Width; NewFramebuffer->Height = Height; @@ -2158,7 +1932,7 @@ FEFramebuffer* FEResourceManager::CreateFramebuffer(const int Attachments, const if (Attachments & FE_STENCIL_ATTACHMENT) { - //to-do: make it correct + //to-do: make TextureIterator correct NewFramebuffer->SetStencilAttachment(new FETexture(Width, Height, FreeObjectName(FE_TEXTURE))); } @@ -2172,8 +1946,6 @@ FEPostProcess* FEResourceManager::CreatePostProcess(const int ScreenWidth, const NewPostProcess->ScreenWidth = ScreenWidth; NewPostProcess->ScreenHeight = ScreenHeight; - NewPostProcess->ScreenQuad = GetMesh("1Y251E6E6T78013635793156"/*"plane"*/); - NewPostProcess->ScreenQuadShader = GetShader("7933272551311F3A1A5B2363"/*"FEScreenQuadShader"*/); // Currently postProcess is not using intermediateFramebuffer colorAttachment directly. NewPostProcess->IntermediateFramebuffer = CreateFramebuffer(0, ScreenWidth, ScreenHeight); @@ -2185,112 +1957,124 @@ std::string FEResourceManager::FreeObjectName(const FE_OBJECT_TYPE ObjectType) std::string Result = "NULL"; switch (ObjectType) { - case FocalEngine::FE_NULL: + case FocalEngine::FE_NULL: + { + return Result; + } + case FocalEngine::FE_SHADER: + { + const size_t NextID = Shaders.size(); + size_t Index = 0; + Result = "Shader_" + std::to_string(NextID + Index); + while (Shaders.find(Result) != Shaders.end()) { - return Result; + Index++; + Result = "Shader_" + std::to_string(NextID + Index); } - case FocalEngine::FE_SHADER: - { - const size_t NextID = Shaders.size() > StandardShaders.size() ? Shaders.size() : StandardShaders.size(); - size_t Index = 0; - Result = "shader_" + std::to_string(NextID + Index); - while (Shaders.find(Result) != Shaders.end() || StandardShaders.find(Result) != StandardShaders.end()) - { - Index++; - Result = "shader_" + std::to_string(NextID + Index); - } - return Result; - } - case FocalEngine::FE_TEXTURE: + return Result; + } + case FocalEngine::FE_TEXTURE: + { + const size_t NextID = Textures.size(); + size_t Index = 0; + Result = "Texture_" + std::to_string(NextID + Index); + while (Textures.find(Result) != Textures.end()) { - const size_t NextID = Textures.size() > StandardTextures.size() ? Textures.size() : StandardTextures.size(); - size_t index = 0; - Result = "texture_" + std::to_string(NextID + index); - while (Textures.find(Result) != Textures.end() || StandardTextures.find(Result) != StandardTextures.end()) - { - index++; - Result = "texture_" + std::to_string(NextID + index); - } - - return Result; + Index++; + Result = "Texture_" + std::to_string(NextID + Index); } - case FocalEngine::FE_MESH: - { - const size_t NextID = Meshes.size() > StandardMeshes.size() ? Meshes.size() : StandardMeshes.size(); - size_t index = 0; - Result = "mesh_" + std::to_string(NextID + index); - while (Meshes.find(Result) != Meshes.end() || StandardMeshes.find(Result) != StandardMeshes.end()) - { - index++; - Result = "mesh_" + std::to_string(NextID + index); - } - return Result; - } - case FocalEngine::FE_MATERIAL: + return Result; + } + case FocalEngine::FE_MESH: + { + const size_t NextID = Meshes.size(); + size_t Index = 0; + Result = "Mesh_" + std::to_string(NextID + Index); + while (Meshes.find(Result) != Meshes.end()) { - const size_t NextID = Materials.size() > StandardMaterials.size() ? Materials.size() : StandardMaterials.size(); - size_t index = 0; - Result = "material_" + std::to_string(NextID + index); - while (Materials.find(Result) != Materials.end() || StandardMaterials.find(Result) != StandardMaterials.end()) - { - index++; - Result = "material_" + std::to_string(NextID + index); - } - - return Result; + Index++; + Result = "Mesh_" + std::to_string(NextID + Index); } - case FocalEngine::FE_GAMEMODEL: - { - const size_t NextID = GameModels.size() > StandardGameModels.size() ? GameModels.size() : StandardGameModels.size(); - size_t index = 0; - Result = "gameModel_" + std::to_string(NextID + index); - while (GameModels.find(Result) != GameModels.end() || StandardGameModels.find(Result) != StandardGameModels.end()) - { - index++; - Result = "gameModel_" + std::to_string(NextID + index); - } - return Result; - } - case FocalEngine::FE_ENTITY: - { - return Result; - } - case FocalEngine::FE_TERRAIN: + return Result; + } + case FocalEngine::FE_MATERIAL: + { + const size_t NextID = Materials.size(); + size_t Index = 0; + Result = "Material_" + std::to_string(NextID + Index); + while (Materials.find(Result) != Materials.end()) { - return Result; + Index++; + Result = "Material_" + std::to_string(NextID + Index); } - case FocalEngine::FE_ENTITY_INSTANCED: + + return Result; + } + case FocalEngine::FE_GAMEMODEL: + { + const size_t NextID = GameModels.size(); + size_t Index = 0; + Result = "GameModel_" + std::to_string(NextID + Index); + while (GameModels.find(Result) != GameModels.end()) { - return Result; + Index++; + Result = "GameModel_" + std::to_string(NextID + Index); } - default: - { - return Result; - } + + return Result; + } + case FocalEngine::FE_ENTITY: + { + return Result; + } + + default: + { + return Result; + } } } -FETexture* FEResourceManager::CreateSameFormatTexture(FETexture* ExampleTexture, const int DifferentW, const int DifferentH, const bool bUnManaged, const std::string Name) +FETexture* FEResourceManager::CreateSameFormatTexture(FETexture* ReferenceTexture, const int DifferentW, const int DifferentH, const bool bUnManaged, const std::string Name) { - if (ExampleTexture == nullptr) + if (ReferenceTexture == nullptr) { - LOG.Add("FEResourceManager::CreateSameFormatTexture called with nullptr pointer as exampleTexture", "FE_LOG_RENDERING", FE_LOG_ERROR); + LOG.Add("FEResourceManager::CreateSameFormatTexture called with nullptr pointer as ReferenceTexture", "FE_LOG_RENDERING", FE_LOG_ERROR); return nullptr; } if (DifferentW == 0 && DifferentH == 0) - return CreateTexture(ExampleTexture->InternalFormat, ExampleTexture->Format, ExampleTexture->Width, ExampleTexture->Height, bUnManaged, Name); - + return CreateTexture(ReferenceTexture->InternalFormat, ReferenceTexture->Format, ReferenceTexture->Width, ReferenceTexture->Height, bUnManaged, Name); + if (DifferentW != 0 && DifferentH == 0) - return CreateTexture(ExampleTexture->InternalFormat, ExampleTexture->Format, DifferentW, ExampleTexture->Height, bUnManaged, Name); - + return CreateTexture(ReferenceTexture->InternalFormat, ReferenceTexture->Format, DifferentW, ReferenceTexture->Height, bUnManaged, Name); + if (DifferentW == 0 && DifferentH != 0) - return CreateTexture(ExampleTexture->InternalFormat, ExampleTexture->Format, ExampleTexture->Width, DifferentH, bUnManaged, Name); - - return CreateTexture(ExampleTexture->InternalFormat, ExampleTexture->Format, DifferentW, DifferentH, bUnManaged, Name); + return CreateTexture(ReferenceTexture->InternalFormat, ReferenceTexture->Format, ReferenceTexture->Width, DifferentH, bUnManaged, Name); + + return CreateTexture(ReferenceTexture->InternalFormat, ReferenceTexture->Format, DifferentW, DifferentH, bUnManaged, Name); +} + +// TO-DO: Ensure all formats are supported. +FETexture* FEResourceManager::CreateCopyOfTexture(FETexture* ReferenceTexture, bool bUnManaged, std::string Name) +{ + FETexture* Result = nullptr; + if (ReferenceTexture == nullptr) + { + LOG.Add("FEResourceManager::CreateCopyOfTexture called with nullptr pointer as ReferenceTexture", "FE_LOG_RENDERING", FE_LOG_ERROR); + return Result; + } + + unsigned char* ReferenceRawData = ReferenceTexture->GetRawData(); + Result = CreateSameFormatTexture(ReferenceTexture, 0, 0, bUnManaged, Name); + + FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, Result->TextureID)); + FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, Result->InternalFormat, Result->Width, Result->Height, 0, GL_RGB, GL_UNSIGNED_BYTE, ReferenceRawData); + + return Result; } void FEResourceManager::AddTextureToManaged(FETexture* Texture) @@ -2312,21 +2096,23 @@ void FEResourceManager::AddTextureToManaged(FETexture* Texture) void FEResourceManager::ReSaveStandardMeshes() { - const std::vector StandardMeshes = GetStandardMeshList(); - for (size_t i = 0; i < StandardMeshes.size(); i++) + auto MeshIterator = Meshes.begin(); + while (MeshIterator != Meshes.end()) { - FEMesh* CurrentMesh = GetMesh(StandardMeshes[i]); - SaveFEMesh(CurrentMesh, (ResourcesFolder + CurrentMesh->GetObjectID() + std::string(".model")).c_str()); + if (MeshIterator->second->GetTag() == ENGINE_RESOURCE_TAG) + SaveFEMesh(MeshIterator->second, (ResourcesFolder + MeshIterator->second->GetObjectID() + std::string(".model")).c_str()); + MeshIterator++; } } -void FEResourceManager::ReSaveStandardTextures() +void FEResourceManager::ReSaveEnginePrivateTextures() { - auto it = StandardTextures.begin(); - while (it != StandardTextures.end()) + auto TextureIterator = Textures.begin(); + while (TextureIterator != Textures.end()) { - SaveFETexture(it->second, (ResourcesFolder + it->second->GetObjectID() + std::string(".texture")).c_str()); - it++; + if (TextureIterator->second->GetTag() == ENGINE_RESOURCE_TAG) + SaveFETexture(TextureIterator->second, (ResourcesFolder + TextureIterator->second->GetObjectID() + std::string(".texture")).c_str()); + TextureIterator++; } } @@ -2354,7 +2140,7 @@ std::string FEResourceManager::GetDefaultResourcesFolder() std::vector FEResourceManager::ChannelsToFETextures(FETexture* SourceTexture) { - std::vector result; + std::vector Result; size_t TextureDataLenght = 0; const unsigned char* pixels = SourceTexture->GetRawData(&TextureDataLenght); @@ -2387,17 +2173,17 @@ std::vector FEResourceManager::ChannelsToFETextures(FETexture* Sourc AlphaChannel[index++] = pixels[i]; } - result.push_back(RawDataToFETexture(RedChannel, SourceTexture->GetWidth(), SourceTexture->GetHeight(), GL_RED, GL_RED)); - result.back()->SetName(SourceTexture->GetName() + "_R"); + Result.push_back(RawDataToFETexture(RedChannel, SourceTexture->GetWidth(), SourceTexture->GetHeight(), GL_RED, GL_RED)); + Result.back()->SetName(SourceTexture->GetName() + "_R"); - result.push_back(RawDataToFETexture(GreenChannel, SourceTexture->GetWidth(), SourceTexture->GetHeight(), GL_RED, GL_RED)); - result.back()->SetName(SourceTexture->GetName() + "_G"); + Result.push_back(RawDataToFETexture(GreenChannel, SourceTexture->GetWidth(), SourceTexture->GetHeight(), GL_RED, GL_RED)); + Result.back()->SetName(SourceTexture->GetName() + "_G"); - result.push_back(RawDataToFETexture(BlueChannel, SourceTexture->GetWidth(), SourceTexture->GetHeight(), GL_RED, GL_RED)); - result.back()->SetName(SourceTexture->GetName() + "_B"); + Result.push_back(RawDataToFETexture(BlueChannel, SourceTexture->GetWidth(), SourceTexture->GetHeight(), GL_RED, GL_RED)); + Result.back()->SetName(SourceTexture->GetName() + "_B"); - result.push_back(RawDataToFETexture(AlphaChannel, SourceTexture->GetWidth(), SourceTexture->GetHeight(), GL_RED, GL_RED)); - result.back()->SetName(SourceTexture->GetName() + "_A"); + Result.push_back(RawDataToFETexture(AlphaChannel, SourceTexture->GetWidth(), SourceTexture->GetHeight(), GL_RED, GL_RED)); + Result.back()->SetName(SourceTexture->GetName() + "_A"); delete[] pixels; delete[] RedChannel; @@ -2405,536 +2191,138 @@ std::vector FEResourceManager::ChannelsToFETextures(FETexture* Sourc delete[] BlueChannel; delete[] AlphaChannel; - return result; + return Result; } -void FEResourceManager::ActivateTerrainVacantLayerSlot(FETerrain* Terrain, FEMaterial* Material) +bool FEResourceManager::ExportFETextureToPNG(FETexture* TextureToExport, const char* FileName) { - if (Terrain == nullptr) + if (TextureToExport == nullptr) { - LOG.Add("FEResourceManager::activateTerrainVacantLayerSlot with nullptr terrain", "FE_LOG_GENERAL", FE_LOG_WARNING); - return; + LOG.Add("FEResourceManager::ExportFETextureToPNG with nullptr TextureToExport", "FE_LOG_SAVING", FE_LOG_ERROR); + return false; } - - // If this terrain does not have layerMaps we would create them. - if (Terrain->LayerMaps[0] == nullptr) - { - FETexture* NewTexture = CreateTexture(); - NewTexture->Width = FE_TERRAIN_STANDARD_LAYER_MAP_RESOLUTION; - NewTexture->Height = FE_TERRAIN_STANDARD_LAYER_MAP_RESOLUTION; - NewTexture->InternalFormat = GL_RGBA; - std::vector RawData; - const size_t DataLenght = NewTexture->GetWidth() * NewTexture->GetHeight() * 4; - RawData.resize(DataLenght); - for (size_t i = 0; i < DataLenght; i++) - { - RawData[i] = 0; - } + if (TextureToExport->InternalFormat != GL_RGBA && + TextureToExport->InternalFormat != GL_RED && + TextureToExport->InternalFormat != GL_R16 && + TextureToExport->InternalFormat != GL_COMPRESSED_RGBA_S3TC_DXT5_EXT && + TextureToExport->InternalFormat != GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && + TextureToExport->InternalFormat != GL_RGBA16F && + TextureToExport->InternalFormat != GL_RG16F) + { + LOG.Add("FEResourceManager::ExportFETextureToPNG InternalFormat of TextureToExport is not supported", "FE_LOG_SAVING", FE_LOG_ERROR); + return false; + } - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); - FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, GL_RGBA, NewTexture->Width, NewTexture->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, RawData.data()); + auto HalfFloatToFloat = [](unsigned short HalfFloat) -> float { + int Sign = (HalfFloat >> 15) & 0x00000001; + int Exponent = (HalfFloat >> 10) & 0x0000001F; + int Mantissa = HalfFloat & 0x000003FF; - FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); + Exponent = Exponent + (127 - 15); + int FloatValue = (Sign << 31) | (Exponent << 23) | (Mantissa << 13); - Terrain->LayerMaps[0] = NewTexture; + return *reinterpret_cast(&FloatValue); + }; - NewTexture = CreateTexture(); - NewTexture->Width = FE_TERRAIN_STANDARD_LAYER_MAP_RESOLUTION; - NewTexture->Height = FE_TERRAIN_STANDARD_LAYER_MAP_RESOLUTION; - NewTexture->InternalFormat = GL_RGBA; + std::vector RawData; + if (TextureToExport->InternalFormat == GL_RGBA16F) + { + const unsigned char* TextureData = TextureToExport->GetRawData(); + RawData.resize(TextureToExport->GetWidth() * TextureToExport->GetHeight() * 4); - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); - FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, GL_RGBA, NewTexture->Width, NewTexture->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, RawData.data()); + size_t RawDataIndex = 0; + for (size_t i = 0; i < RawData.size() * sizeof(unsigned short); i += 2) + { + // Combine two bytes into one 16-bit half float. + unsigned short Half = (TextureData[i + 1] << 8) | TextureData[i]; + float Value = HalfFloatToFloat(Half); - FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); + // Clamp and scale the floating-point value to a byte. + unsigned char ByteValue = static_cast(std::max(0.0f, std::min(1.0f, Value)) * 255.0f); + RawData[RawDataIndex++] = ByteValue; + } - Terrain->LayerMaps[1] = NewTexture; + // Flip image vertically + const size_t RowBytes = TextureToExport->GetWidth() * 4; + unsigned char* RowBuffer = new unsigned char[RowBytes]; + for (size_t y = 0; y < TextureToExport->GetHeight() / 2; y++) + { + // Copy the top row to a buffer + std::memcpy(RowBuffer, RawData.data() + y * RowBytes, RowBytes); - Terrain->ActivateVacantLayerSlot(Material); - FillTerrainLayerMask(Terrain, 0); - return; - } + // Copy the bottom row to the top + std::memcpy(RawData.data() + y * RowBytes, RawData.data() + (TextureToExport->GetHeight() - 1 - y) * RowBytes, RowBytes); - Terrain->ActivateVacantLayerSlot(Material); -} + // Copy the buffer contents (original top row) to the bottom + std::memcpy(RawData.data() + (TextureToExport->GetHeight() - 1 - y) * RowBytes, RowBuffer, RowBytes); + } -void FEResourceManager::FillTerrainLayerMaskWithRawData(const unsigned char* RawData, const FETerrain* Terrain, const size_t LayerIndex) -{ - if (RawData == nullptr) - { - LOG.Add("FEResourceManager::fillTerrainLayerMaskWithRawData with nullptr rawData", "FE_LOG_GENERAL", FE_LOG_WARNING); - return; + delete[] RowBuffer; } - - if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + else if (TextureToExport->InternalFormat == GL_RG16F) { - LOG.Add("FEResourceManager::fillTerrainLayerMaskWithRawData with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } - - int Index = 0; - const size_t TextureWidht = Terrain->LayerMaps[0]->GetWidth(); - const size_t TextureHeight = Terrain->LayerMaps[0]->GetHeight(); + const unsigned char* TextureData = TextureToExport->GetRawData(); + // Two channels per pixel (R and G) – output 8 bits per channel. + RawData.resize(TextureToExport->GetWidth() * TextureToExport->GetHeight() * 2); - std::vector LayersPerTextureData; - LayersPerTextureData.resize(2); - LayersPerTextureData[0] = Terrain->LayerMaps[0]->GetRawData(); - LayersPerTextureData[1] = Terrain->LayerMaps[1]->GetRawData(); + size_t RawDataIndex = 0; + // Total half-floats = width * height * 2; each half-float is 2 bytes. + for (size_t i = 0; i < RawData.size() * sizeof(unsigned short); i += 2) + { + unsigned short Half = (TextureData[i + 1] << 8) | TextureData[i]; + float Value = HalfFloatToFloat(Half); + unsigned char ByteValue = static_cast(std::max(0.0f, std::min(1.0f, Value)) * 255.0f); + RawData[RawDataIndex++] = ByteValue; + } - std::vector LayersPerChannelData; - LayersPerChannelData.resize(FE_TERRAIN_MAX_LAYERS); - for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) - { - LayersPerChannelData[i] = new unsigned char[TextureWidht * TextureHeight]; + // Flip image vertically. + const size_t RowBytes = TextureToExport->GetWidth() * 2; // 2 bytes per pixel row + unsigned char* RowBuffer = new unsigned char[RowBytes]; + for (size_t y = 0; y < TextureToExport->GetHeight() / 2; y++) + { + std::memcpy(RowBuffer, RawData.data() + y * RowBytes, RowBytes); + std::memcpy(RawData.data() + y * RowBytes, RawData.data() + (TextureToExport->GetHeight() - 1 - y) * RowBytes, RowBytes); + std::memcpy(RawData.data() + (TextureToExport->GetHeight() - 1 - y) * RowBytes, RowBuffer, RowBytes); + } + delete[] RowBuffer; } + else if (TextureToExport->InternalFormat == GL_RED) + { + RawData.resize(TextureToExport->GetWidth() * TextureToExport->GetHeight() * 4); + const unsigned char* TextreData = TextureToExport->GetRawData(); - for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + for (size_t i = 0; i < RawData.size(); i += 4) + { + RawData[i] = TextreData[i / 4]; + RawData[i + 1] = TextreData[i / 4]; + RawData[i + 2] = TextreData[i / 4]; + RawData[i + 3] = 255; + } + } + else if (TextureToExport->InternalFormat == GL_R16) { - Index = 0; - if (LayerIndex == i) + RawData.resize(TextureToExport->GetWidth() * TextureToExport->GetHeight() * 2); + const unsigned char* TextreData = TextureToExport->GetRawData(); + + for (size_t i = 0; i < RawData.size(); i++) { - for (size_t j = 0; j < TextureWidht * TextureHeight; j++) - { - LayersPerChannelData[i][Index++] = RawData[j]; - } + RawData[i] = TextreData[i]; } - else + + for (size_t i = 0; i < RawData.size(); i += 2) { - for (size_t j = i % FE_TERRAIN_LAYER_PER_TEXTURE; j < TextureWidht * TextureHeight * 4; j += 4) - { - LayersPerChannelData[i][Index++] = LayersPerTextureData[i / FE_TERRAIN_LAYER_PER_TEXTURE][j]; - } + std::swap(RawData[i], RawData[i + 1]); } } - - std::vector FinalTextureChannels; - FinalTextureChannels.resize(2); - FinalTextureChannels[0] = new unsigned char[TextureWidht * TextureHeight * 4]; - FinalTextureChannels[1] = new unsigned char[TextureWidht * TextureHeight * 4]; - - Index = 0; - - int* AllChannelsPixels = new int[8]; - - for (size_t i = 0; i < TextureWidht * TextureHeight * 4; i += 4) + else { - float sum = 0.0f; - for (size_t j = 0; j < 8; j++) + RawData.resize(TextureToExport->GetWidth() * TextureToExport->GetHeight() * 4); + const unsigned char* TextreData = TextureToExport->GetRawData(); + + for (size_t i = 0; i < RawData.size(); i++) { - AllChannelsPixels[j] = LayersPerChannelData[j][Index]; - } - - //int amountOfOverIntansity = sum - 255; - //for (size_t j = 0; j < 8; j++) - //{ - // if (j == layerIndex) - // continue; - - // allChannelsPixels[j] -= allChannelsPixels[layerIndex]; - // if (allChannelsPixels[j] < 0) - // allChannelsPixels[j] = 0; - //} - - //sum = 0.0f; - //for (size_t j = 0; j < 8; j++) - //{ - // sum += allChannelsPixels[j]; - //} - - //sum /= 255; - //if (sum < 1.0f) - //{ - // allChannelsPixels[0] += int((1.0f - sum) * 255); - //} - - FinalTextureChannels[0][i] = static_cast(AllChannelsPixels[0]); - FinalTextureChannels[0][i + 1] = static_cast(AllChannelsPixels[1]); - FinalTextureChannels[0][i + 2] = static_cast(AllChannelsPixels[2]); - FinalTextureChannels[0][i + 3] = static_cast(AllChannelsPixels[3]); - - FinalTextureChannels[1][i] = static_cast(AllChannelsPixels[4]); - FinalTextureChannels[1][i + 1] = static_cast(AllChannelsPixels[5]); - FinalTextureChannels[1][i + 2] = static_cast(AllChannelsPixels[6]); - FinalTextureChannels[1][i + 3] = static_cast(AllChannelsPixels[7]); - - Index++; - } - - const int MaxDimention = std::max(static_cast(TextureWidht), static_cast(TextureHeight)); - const size_t MipCount = static_cast(floor(log2(MaxDimention)) + 1); - - Terrain->LayerMaps[0]->UpdateRawData(FinalTextureChannels[0], MipCount); - FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); - - Terrain->LayerMaps[1]->UpdateRawData(FinalTextureChannels[1], MipCount); - FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); - - delete[] LayersPerTextureData[0]; - delete[] LayersPerTextureData[1]; - for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) - { - delete[]LayersPerChannelData[i]; - } - - delete[] FinalTextureChannels[0]; - delete[] FinalTextureChannels[1]; - delete[] AllChannelsPixels; -} - -void FEResourceManager::LoadTerrainLayerMask(const char* FileName, FETerrain* Terrain, const size_t LayerIndex) -{ - if (Terrain == nullptr) - { - LOG.Add("FEResourceManager::loadTerrainLayerMask with nullptr terrain", "FE_LOG_GENERAL", FE_LOG_WARNING); - return; - } - - if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) - { - LOG.Add("FEResourceManager::loadTerrainLayerMask with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } - - if (Terrain->Layers[LayerIndex] == nullptr) - { - LOG.Add("FEResourceManager::loadTerrainLayerMask on indicated layer slot layer is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } - - // Reading data from file. - std::vector RawData; - unsigned UWidth, UHeight; - lodepng::decode(RawData, UWidth, UHeight, FileName); - - if (RawData.empty()) - { - LOG.Add(std::string("can't read file: ") + FileName + " in function FEResourceManager::loadTerrainLayerMask.", "FE_LOG_LOADING", FE_LOG_ERROR); - return; - } - - // It should be just ordinary png not gray scale. - if (RawData.size() != UWidth * UHeight * 4) - { - LOG.Add(std::string("can't use file: ") + FileName + " in function FEResourceManager::loadTerrainLayerMask as a mask.", "FE_LOG_LOADING", FE_LOG_ERROR); - return; - } - - // If new texture have different resolution. - FETexture* FirstLayerMap = Terrain->LayerMaps[0]; - if (UWidth != FirstLayerMap->GetWidth() || UHeight != FirstLayerMap->GetHeight()) - { - bool bNeedToResizeMaskTexture = false; - // Firstly we check if current masks has any data. - std::vector LayersPerTextureData; - LayersPerTextureData.resize(2); - LayersPerTextureData[0] = Terrain->LayerMaps[0]->GetRawData(); - LayersPerTextureData[1] = Terrain->LayerMaps[1]->GetRawData(); - - // We fill first layer by default so we should check it differently - const unsigned char FirstValue = LayersPerTextureData[0][0]; - for (size_t i = 0; i < static_cast(Terrain->LayerMaps[0]->GetWidth() * Terrain->LayerMaps[0]->GetHeight()); i+=4) - { - if (LayersPerTextureData[0][i] != FirstValue || LayersPerTextureData[0][i + 1] != 0 || - LayersPerTextureData[0][i + 2] != 0 || LayersPerTextureData[0][i + 3] != 0 || - LayersPerTextureData[1][i] != 0 || LayersPerTextureData[1][i + 1] != 0 || - LayersPerTextureData[1][i + 2] != 0 || LayersPerTextureData[1][i + 3] != 0) - { - bNeedToResizeMaskTexture = true; - break; - } - } - - if (bNeedToResizeMaskTexture) - { - LOG.Add("FEResourceManager::loadTerrainLayerMask resizing loaded mask to match currently used one.", "FE_LOG_LOADING", FE_LOG_WARNING); - const unsigned char* NewRawData = ResizeTextureRawData(RawData.data(), UWidth, UHeight, FirstLayerMap->GetWidth(), FirstLayerMap->GetHeight(), GL_RGBA, 1); - if (NewRawData == nullptr) - { - LOG.Add("FEResourceManager::loadTerrainLayerMask resizing loaded mask failed.", "FE_LOG_LOADING", FE_LOG_ERROR); - return; - } - - RawData.clear(); - - for (size_t i = 0; i < static_cast(FirstLayerMap->GetWidth() * FirstLayerMap->GetHeight() * 4); i++) - { - RawData.push_back(NewRawData[i]); - } - - delete[] NewRawData; - } - else - { - LOG.Add("FEResourceManager::loadTerrainLayerMask resizing terrainLayerMap to match currently loaded one.", "FE_LOG_LOADING", FE_LOG_WARNING); - - FETexture* NewTexture = CreateTexture(); - NewTexture->Width = UWidth; - NewTexture->Height = UHeight; - NewTexture->InternalFormat = GL_RGBA; - - std::vector RawData; - const size_t DataLenght = NewTexture->GetWidth() * NewTexture->GetHeight() * 4; - RawData.resize(DataLenght); - for (size_t i = 0; i < DataLenght; i++) - { - RawData[i] = 0; - } - - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); - FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, GL_RGBA, NewTexture->Width, NewTexture->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, RawData.data()); - - FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); - - DeleteFETexture(Terrain->LayerMaps[0]); - Terrain->LayerMaps[0] = NewTexture; - FirstLayerMap = Terrain->LayerMaps[0]; - - NewTexture = CreateTexture(); - NewTexture->Width = UWidth; - NewTexture->Height = UHeight; - NewTexture->InternalFormat = GL_RGBA; - - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); - FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, GL_RGBA, NewTexture->Width, NewTexture->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, RawData.data()); - - FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); - - DeleteFETexture(Terrain->LayerMaps[1]); - Terrain->LayerMaps[1] = NewTexture; - } - } - - unsigned char* FilledChannel = new unsigned char[FirstLayerMap->GetWidth() * FirstLayerMap->GetHeight()]; - int Index = 0; - for (size_t i = 0; i < static_cast(FirstLayerMap->GetWidth() * FirstLayerMap->GetHeight() * 4); i += 4) - { - FilledChannel[Index++] = RawData[i]; - } - - FillTerrainLayerMaskWithRawData(FilledChannel, Terrain, LayerIndex); -} - -void FEResourceManager::SaveTerrainLayerMask(const char* FileName, const FETerrain* Terrain, const size_t LayerIndex) -{ - if (Terrain == nullptr) - { - LOG.Add("FEResourceManager::loadTerrainLayerMask with nullptr terrain", "FE_LOG_GENERAL", FE_LOG_WARNING); - return; - } - - if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) - { - LOG.Add("FEResourceManager::loadTerrainLayerMask with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } - - if (Terrain->Layers[LayerIndex] == nullptr) - { - LOG.Add("FEResourceManager::loadTerrainLayerMask on indicated layer slot layer is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } - - // Reading data from current layer map texture. - size_t ResultingTextureDataLenght = 0; - FETexture* CorrectLayer = Terrain->LayerMaps[LayerIndex / FE_TERRAIN_LAYER_PER_TEXTURE]; - const unsigned char* RawData = CorrectLayer->GetRawData(&ResultingTextureDataLenght); - unsigned char* ResultingData = new unsigned char[ResultingTextureDataLenght]; - - for (size_t i = 0; i < ResultingTextureDataLenght; i += 4) - { - const size_t index = i + LayerIndex % FE_TERRAIN_LAYER_PER_TEXTURE; - ResultingData[i] = RawData[index]; - ResultingData[i + 1] = RawData[index]; - ResultingData[i + 2] = RawData[index]; - ResultingData[i + 3] = 255; - } - - ExportRawDataToPNG(FileName, ResultingData, CorrectLayer->GetWidth(), CorrectLayer->GetHeight(), GL_RGBA); -} - -void FEResourceManager::FillTerrainLayerMask(const FETerrain* Terrain, const size_t LayerIndex) -{ - if (Terrain == nullptr) - { - LOG.Add("FEResourceManager::fillTerrainLayerMask with nullptr terrain", "FE_LOG_GENERAL", FE_LOG_WARNING); - return; - } - - if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) - { - LOG.Add("FEResourceManager::fillTerrainLayerMask with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } - - if (Terrain->Layers[LayerIndex] == nullptr) - { - LOG.Add("FEResourceManager::fillTerrainLayerMask on indicated layer slot layer is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } - - FETexture* CorrectLayer = Terrain->LayerMaps[LayerIndex / FE_TERRAIN_LAYER_PER_TEXTURE]; - const size_t TextureWidht = CorrectLayer->GetWidth(); - const size_t TextureHeight = CorrectLayer->GetHeight(); - unsigned char* FilledChannel = new unsigned char[TextureWidht * TextureHeight]; - for (size_t i = 0; i < TextureWidht * TextureHeight; i++) - { - FilledChannel[i] = 255; - } - - FillTerrainLayerMaskWithRawData(FilledChannel, Terrain, LayerIndex); - delete[] FilledChannel; - -} - -void FEResourceManager::ClearTerrainLayerMask(const FETerrain* Terrain, const size_t LayerIndex) -{ - if (Terrain == nullptr) - { - LOG.Add("FEResourceManager::clearTerrainLayerMask with nullptr terrain", "FE_LOG_GENERAL", FE_LOG_WARNING); - return; - } - - if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) - { - LOG.Add("FEResourceManager::clearTerrainLayerMask with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } - - if (Terrain->Layers[LayerIndex] == nullptr) - { - LOG.Add("FEResourceManager::clearTerrainLayerMask on indicated layer slot layer is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } - - FETexture* CorrectLayer = Terrain->LayerMaps[LayerIndex / FE_TERRAIN_LAYER_PER_TEXTURE]; - const size_t TextureWidht = CorrectLayer->GetWidth(); - const size_t TextureHeight = CorrectLayer->GetHeight(); - unsigned char* FilledChannel = new unsigned char[TextureWidht * TextureHeight]; - for (size_t i = 0; i < TextureWidht * TextureHeight; i++) - { - FilledChannel[i] = 0; - } - - FillTerrainLayerMaskWithRawData(FilledChannel, Terrain, LayerIndex); - delete[] FilledChannel; -} - -bool FEResourceManager::ExportFETextureToPNG(FETexture* TextureToExport, const char* FileName) -{ - if (TextureToExport == nullptr) - { - LOG.Add("FEResourceManager::exportFETextureToPNG with nullptr textureToExport", "FE_LOG_SAVING", FE_LOG_ERROR); - return false; - } - - if (TextureToExport->InternalFormat != GL_RGBA && - TextureToExport->InternalFormat != GL_RED && - TextureToExport->InternalFormat != GL_R16 && - TextureToExport->InternalFormat != GL_COMPRESSED_RGBA_S3TC_DXT5_EXT && - TextureToExport->InternalFormat != GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && - TextureToExport->InternalFormat != GL_RGBA16F) - { - LOG.Add("FEResourceManager::exportFETextureToPNG internalFormat of textureToExport is not supported", "FE_LOG_SAVING", FE_LOG_ERROR); - return false; - } - - std::vector RawData; - - if (TextureToExport->InternalFormat == GL_RGBA16F) - { - auto HalfFloatToFloat = [](unsigned short HalfFloat) -> float { - int Sign = (HalfFloat >> 15) & 0x00000001; - int Exponent = (HalfFloat >> 10) & 0x0000001F; - int Mantissa = HalfFloat & 0x000003FF; - - Exponent = Exponent + (127 - 15); - int FloatValue = (Sign << 31) | (Exponent << 23) | (Mantissa << 13); - - return *reinterpret_cast(&FloatValue); - }; - - const unsigned char* TextureData = TextureToExport->GetRawData(); - RawData.resize(TextureToExport->GetWidth() * TextureToExport->GetHeight() * 4); - - size_t RawDataIndex = 0; - for (size_t i = 0; i < RawData.size() * sizeof(unsigned short); i+=2) - { - // Combine two bytes into one 16-bit half float. - unsigned short Half = (TextureData[i + 1] << 8) | TextureData[i]; - float Value = HalfFloatToFloat(Half); - - // Clamp and scale the floating-point value to a byte. - unsigned char ByteValue = static_cast(std::max(0.0f, std::min(1.0f, Value)) * 255.0f); - RawData[RawDataIndex++] = ByteValue; - } - - // Flip image vertically - const size_t RowBytes = TextureToExport->GetWidth() * 4; - unsigned char* RowBuffer = new unsigned char[RowBytes]; - - for (size_t y = 0; y < TextureToExport->GetHeight() / 2; y++) - { - // Copy the top row to a buffer - std::memcpy(RowBuffer, RawData.data() + y * RowBytes, RowBytes); - - // Copy the bottom row to the top - std::memcpy(RawData.data() + y * RowBytes, RawData.data() + (TextureToExport->GetHeight() - 1 - y) * RowBytes, RowBytes); - - // Copy the buffer contents (original top row) to the bottom - std::memcpy(RawData.data() + (TextureToExport->GetHeight() - 1 - y) * RowBytes, RowBuffer, RowBytes); - } - - delete[] RowBuffer; - } - else if (TextureToExport->InternalFormat == GL_RED) - { - RawData.resize(TextureToExport->GetWidth() * TextureToExport->GetHeight() * 4); - const unsigned char* TextreData = TextureToExport->GetRawData(); - - for (size_t i = 0; i < RawData.size(); i += 4) - { - RawData[i] = TextreData[i / 4]; - RawData[i + 1] = TextreData[i / 4]; - RawData[i + 2] = TextreData[i / 4]; - RawData[i + 3] = 255; - } - } - else if (TextureToExport->InternalFormat == GL_R16) - { - RawData.resize(TextureToExport->GetWidth() * TextureToExport->GetHeight() * 2); - const unsigned char* TextreData = TextureToExport->GetRawData(); - - for (size_t i = 0; i < RawData.size(); i++) - { - RawData[i] = TextreData[i]; - } - - for (size_t i = 0; i < RawData.size(); i+=2) - { - std::swap(RawData[i], RawData[i + 1]); - } - } - else - { - RawData.resize(TextureToExport->GetWidth() * TextureToExport->GetHeight() * 4); - const unsigned char* TextreData = TextureToExport->GetRawData(); - - for (size_t i = 0; i < RawData.size(); i++) - { - RawData[i] = TextreData[i]; + RawData[i] = TextreData[i]; } } @@ -2944,6 +2332,10 @@ bool FEResourceManager::ExportFETextureToPNG(FETexture* TextureToExport, const c { Error = lodepng::encode(FilePath, RawData, TextureToExport->GetWidth(), TextureToExport->GetHeight(), LCT_GREY, 16); } + if (TextureToExport->InternalFormat == GL_RG16F) + { + Error = lodepng::encode(FilePath, RawData, TextureToExport->GetWidth(), TextureToExport->GetHeight(), LCT_GREY_ALPHA); + } else { Error = lodepng::encode(FilePath, RawData, TextureToExport->GetWidth(), TextureToExport->GetHeight()); @@ -3114,7 +2506,7 @@ unsigned char* FEResourceManager::ResizeTextureRawData(const unsigned char* Text if (weight > 1.0f) weight = 1.0f; - + size_t sourceIndex = (scaledI * width + scaledJ) * sourceByteCount; if (sourceIndex + 3 >= width * height * sourceByteCount) @@ -3147,10 +2539,10 @@ unsigned char* FEResourceManager::ResizeTextureRawData(const unsigned char* Text newPixel[3] /= pixelsRead; } - result[targetIndex] = newPixel[0]; - result[targetIndex + 1] = newPixel[1]; - result[targetIndex + 2] = newPixel[2]; - result[targetIndex + 3] = newPixel[3];*/ + Result[targetIndex] = newPixel[0]; + Result[targetIndex + 1] = newPixel[1]; + Result[targetIndex + 2] = newPixel[2]; + Result[targetIndex + 3] = newPixel[3];*/ const size_t ScaledI = static_cast(i * ResizeFactorY); const size_t ScaledJ = static_cast(j * ResizeFactorX); @@ -3215,7 +2607,7 @@ void FEResourceManager::ResizeTexture(FETexture* SourceTexture, const int Target FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, SourceTexture->GetTextureID())); const unsigned char* CurrentData = SourceTexture->GetRawData(); - unsigned char* result = ResizeTextureRawData(CurrentData, SourceTexture->GetWidth(), SourceTexture->GetHeight(), TargetWidth, TargetHeight, SourceTexture->InternalFormat, FiltrationLevel); + unsigned char* Result = ResizeTextureRawData(CurrentData, SourceTexture->GetWidth(), SourceTexture->GetHeight(), TargetWidth, TargetHeight, SourceTexture->InternalFormat, FiltrationLevel); SourceTexture->Width = TargetWidth; SourceTexture->Height = TargetHeight; @@ -3224,23 +2616,23 @@ void FEResourceManager::ResizeTexture(FETexture* SourceTexture, const int Target if (SourceTexture->InternalFormat == GL_RGBA) { - SourceTexture->UpdateRawData(result, MipCount); + SourceTexture->UpdateRawData(Result, MipCount); } else if (SourceTexture->InternalFormat == GL_RED) { // Function resizeTextureRawData will output RGBA data, we need to take only R channel. std::vector RedChannel; RedChannel.resize(SourceTexture->GetWidth() * SourceTexture->GetHeight()); - for (size_t i = 0; i < RedChannel.size() * 4; i+=4) + for (size_t i = 0; i < RedChannel.size() * 4; i += 4) { - RedChannel[i / 4] = result[i]; + RedChannel[i / 4] = Result[i]; } SourceTexture->UpdateRawData(RedChannel.data(), MipCount); } else { - SourceTexture->UpdateRawData(result, MipCount); + SourceTexture->UpdateRawData(Result, MipCount); } FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); @@ -3248,147 +2640,36 @@ void FEResourceManager::ResizeTexture(FETexture* SourceTexture, const int Target FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); delete[] CurrentData; - delete[] result; + delete[] Result; } -void FEResourceManager::DeleteTerrainLayerMask(FETerrain* Terrain, const size_t LayerIndex) +FETexture* FEResourceManager::LoadJPGTexture(const char* FileName, const std::string Name) { - if (Terrain == nullptr) - { - LOG.Add("FEResourceManager::deleteTerrainLayerMask with nullptr terrain", "FE_LOG_GENERAL", FE_LOG_WARNING); - return; - } + int UWidth, UHeight, Channels; + const unsigned char* RawData = stbi_load(FileName, &UWidth, &UHeight, &Channels, 0); - if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + if (RawData == nullptr) { - LOG.Add("FEResourceManager::deleteTerrainLayerMask with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; + LOG.Add(std::string("can't load file: ") + FileName + " in function FEResourceManager::LoadJPGTexture.", "FE_LOG_LOADING", FE_LOG_ERROR); + return GetTexture("48271F005A73241F5D7E7134"); // "noTexture" } - if (Terrain->Layers[LayerIndex] == nullptr) - { - LOG.Add("FEResourceManager::deleteTerrainLayerMask on indicated layer slot layer is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); - return; - } + FETexture* NewTexture = CreateTexture(Name); + NewTexture->Width = UWidth; + NewTexture->Height = UHeight; - ClearTerrainLayerMask(Terrain, LayerIndex); + const int InternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - std::vector LayersPerTextureData; - LayersPerTextureData.resize(2); - size_t RawDataSize = 0; - LayersPerTextureData[0] = Terrain->LayerMaps[0]->GetRawData(&RawDataSize); - LayersPerTextureData[1] = Terrain->LayerMaps[1]->GetRawData(); + FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); + FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, InternalFormat, NewTexture->Width, NewTexture->Height, 0, GL_RGB, GL_UNSIGNED_BYTE, RawData); + delete RawData; + NewTexture->InternalFormat = InternalFormat; - std::vector AllLayers; - AllLayers.resize(8); - for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) - { - AllLayers[i] = new unsigned char[RawDataSize / 4]; - } - - // Gathering channels from 2 textures. - int ChannelIndex = 0; - for (size_t i = 0; i < RawDataSize; i+=4) - { - AllLayers[0][ChannelIndex] = LayersPerTextureData[0][i]; - AllLayers[1][ChannelIndex] = LayersPerTextureData[0][i + 1]; - AllLayers[2][ChannelIndex] = LayersPerTextureData[0][i + 2]; - AllLayers[3][ChannelIndex] = LayersPerTextureData[0][i + 3]; - - AllLayers[4][ChannelIndex] = LayersPerTextureData[1][i]; - AllLayers[5][ChannelIndex] = LayersPerTextureData[1][i + 1]; - AllLayers[6][ChannelIndex] = LayersPerTextureData[1][i + 2]; - AllLayers[7][ChannelIndex] = LayersPerTextureData[1][i + 3]; - - ChannelIndex++; - } - - // Shifting existing layers masks to place where was deleted mask. - for (size_t i = LayerIndex; i < FE_TERRAIN_MAX_LAYERS - 1; i++) - { - for (size_t j = 0; j < RawDataSize / 4; j++) - { - AllLayers[i][j] = AllLayers[i + 1][j]; - } - } - - unsigned char* FirstTextureData = new unsigned char[RawDataSize]; - unsigned char* SecondTextureData = new unsigned char[RawDataSize]; - - // Putting individual channels back to 2 distinct textures. - ChannelIndex = 0; - for (size_t i = 0; i < RawDataSize; i += 4) - { - FirstTextureData[i] = AllLayers[0][ChannelIndex]; - FirstTextureData[i + 1] = AllLayers[1][ChannelIndex]; - FirstTextureData[i + 2] = AllLayers[2][ChannelIndex]; - FirstTextureData[i + 3] = AllLayers[3][ChannelIndex]; - - SecondTextureData[i] = AllLayers[4][ChannelIndex]; - SecondTextureData[i + 1] = AllLayers[5][ChannelIndex]; - SecondTextureData[i + 2] = AllLayers[6][ChannelIndex]; - SecondTextureData[i + 3] = AllLayers[7][ChannelIndex]; - - ChannelIndex++; - } - - const int MaxDimention = std::max(Terrain->LayerMaps[0]->GetWidth(), Terrain->LayerMaps[0]->GetHeight()); - const size_t MipCount = static_cast(floor(log2(MaxDimention)) + 1); - - Terrain->LayerMaps[0]->UpdateRawData(FirstTextureData, MipCount); - FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); - - Terrain->LayerMaps[1]->UpdateRawData(SecondTextureData, MipCount); - FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); - - for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) - { - delete[] AllLayers[i]; - } - - delete[] FirstTextureData; - delete[] SecondTextureData; - - Terrain->DeleteLayerInSlot(LayerIndex); -} - -FETexture* FEResourceManager::LoadJPGTexture(const char* FileName, const std::string Name) -{ - int UWidth, UHeight, Channels; - const unsigned char* RawData = stbi_load(FileName, &UWidth, &UHeight, &Channels, 0); - - if (RawData == nullptr) - { - LOG.Add(std::string("can't load file: ") + FileName + " in function FEResourceManager::LoadJPGTexture.", "FE_LOG_LOADING", FE_LOG_ERROR); - if (!StandardTextures.empty()) - { - return GetTexture("48271F005A73241F5D7E7134"); // "noTexture" - } - else - { - return nullptr; - } - } - - FETexture* NewTexture = CreateTexture(Name); - NewTexture->Width = UWidth; - NewTexture->Height = UHeight; - - const int InternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - - FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); - FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, InternalFormat, NewTexture->Width, NewTexture->Height, 0, GL_RGB, GL_UNSIGNED_BYTE, RawData); - delete RawData; - NewTexture->InternalFormat = InternalFormat; - - if (NewTexture->MipEnabled) + if (NewTexture->MipEnabled) { FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); // to-do: fix this + // TO-DO: make it configurable. + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); } @@ -3424,7 +2705,7 @@ FETexture* FEResourceManager::LoadBMPTexture(const char* FileName, const std::st void FEResourceManager::CreateMaterialsFromOBJData(std::vector& ResultArray) { std::unordered_map LoadedTextures; - const FEObjLoader& OBJLoader = FEObjLoader::getInstance(); + const FEObjLoader& OBJLoader = FEObjLoader::GetInstance(); for (size_t i = 0; i < OBJLoader.LoadedObjects.size(); i++) { @@ -3433,66 +2714,59 @@ void FEResourceManager::CreateMaterialsFromOBJData(std::vector& Resul FEMaterial* Material = nullptr; std::string Name; - FETexture* Texture = nullptr; if (LoadedTextures.find(OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlbedoMapFile) == LoadedTextures.end() && !OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlbedoMapFile.empty()) { - std::vector LoadedObjects = ImportAsset(OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlbedoMapFile.c_str()); - if (!LoadedObjects.empty() && LoadedObjects[0] != nullptr) + FETexture* LoadedTexture = ImportTexture(OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlbedoMapFile.c_str()); + if (LoadedTexture != nullptr) { - Texture = reinterpret_cast(LoadedObjects[0]); - LoadedTextures[OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlbedoMapFile] = true; Name = FILE_SYSTEM.GetFileName(OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlbedoMapFile.c_str()); - ResultArray.push_back(Texture); + ResultArray.push_back(LoadedTexture); ResultArray.back()->SetName(Name); Material = CreateMaterial(OBJLoader.LoadedObjects[i]->MaterialRecords[0].Name); - Material->SetAlbedoMap(Texture); + Material->SetAlbedoMap(LoadedTexture); } else { - LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlbedoMapFile + " in function FEResourceManager::createMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlbedoMapFile + " in function FEResourceManager::CreateMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); } } if (LoadedTextures.find(OBJLoader.LoadedObjects[i]->MaterialRecords[0].NormalMapFile) == LoadedTextures.end() && !OBJLoader.LoadedObjects[i]->MaterialRecords[0].NormalMapFile.empty()) { - std::vector LoadedObjects = ImportAsset(OBJLoader.LoadedObjects[i]->MaterialRecords[0].NormalMapFile.c_str()); - if (!LoadedObjects.empty() && LoadedObjects[0] != nullptr) + FETexture* LoadedTexture = ImportTexture(OBJLoader.LoadedObjects[i]->MaterialRecords[0].NormalMapFile.c_str()); + if (LoadedTexture != nullptr) { - Texture = reinterpret_cast(LoadedObjects[0]); - LoadedTextures[OBJLoader.LoadedObjects[i]->MaterialRecords[0].NormalMapFile] = true; Name = FILE_SYSTEM.GetFileName(OBJLoader.LoadedObjects[i]->MaterialRecords[0].NormalMapFile.c_str()); - ResultArray.push_back(Texture); + ResultArray.push_back(LoadedTexture); ResultArray.back()->SetName(Name); if (Material == nullptr) Material = CreateMaterial(OBJLoader.LoadedObjects[i]->MaterialRecords[0].Name); - Material->SetNormalMap(Texture); + Material->SetNormalMap(LoadedTexture); } else { - LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].NormalMapFile + " in function FEResourceManager::createMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].NormalMapFile + " in function FEResourceManager::CreateMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); } } if (LoadedTextures.find(OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularMapFile) == LoadedTextures.end() && !OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularMapFile.empty()) { - std::vector LoadedObjects = ImportAsset(OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularMapFile.c_str()); - if (!LoadedObjects.empty() && LoadedObjects[0] != nullptr) + FETexture* LoadedTexture = ImportTexture(OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularMapFile.c_str()); + if (LoadedTexture != nullptr) { - Texture = reinterpret_cast(LoadedObjects[0]); - LoadedTextures[OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularMapFile] = true; Name = FILE_SYSTEM.GetFileName(OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularMapFile.c_str()); - ResultArray.push_back(Texture); + ResultArray.push_back(LoadedTexture); ResultArray.back()->SetName(Name); if (Material == nullptr) @@ -3500,22 +2774,20 @@ void FEResourceManager::CreateMaterialsFromOBJData(std::vector& Resul } else { - LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularMapFile + " in function FEResourceManager::createMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularMapFile + " in function FEResourceManager::CreateMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); } } if (LoadedTextures.find(OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularHighlightMapFile) == LoadedTextures.end() && !OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularHighlightMapFile.empty()) { - std::vector LoadedObjects = ImportAsset(OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularHighlightMapFile.c_str()); - if (!LoadedObjects.empty() && LoadedObjects[0] != nullptr) + FETexture* LoadedTexture = ImportTexture(OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularHighlightMapFile.c_str()); + if (LoadedTexture != nullptr) { - Texture = reinterpret_cast(LoadedObjects[0]); - LoadedTextures[OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularHighlightMapFile] = true; Name = FILE_SYSTEM.GetFileName(OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularHighlightMapFile.c_str()); - ResultArray.push_back(Texture); + ResultArray.push_back(LoadedTexture); ResultArray.back()->SetName(Name); if (Material == nullptr) @@ -3523,22 +2795,20 @@ void FEResourceManager::CreateMaterialsFromOBJData(std::vector& Resul } else { - LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularHighlightMapFile + " in function FEResourceManager::createMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].SpecularHighlightMapFile + " in function FEResourceManager::CreateMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); } } if (LoadedTextures.find(OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlphaMapFile) == LoadedTextures.end() && !OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlphaMapFile.empty()) { - std::vector LoadedObjects = ImportAsset(OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlphaMapFile.c_str()); - if (!LoadedObjects.empty() && LoadedObjects[0] != nullptr) + FETexture* LoadedTexture = ImportTexture(OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlphaMapFile.c_str()); + if (LoadedTexture != nullptr) { - Texture = reinterpret_cast(LoadedObjects[0]); - LoadedTextures[OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlphaMapFile] = true; Name = FILE_SYSTEM.GetFileName(OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlphaMapFile.c_str()); - ResultArray.push_back(Texture); + ResultArray.push_back(LoadedTexture); ResultArray.back()->SetName(Name); if (Material == nullptr) @@ -3546,22 +2816,20 @@ void FEResourceManager::CreateMaterialsFromOBJData(std::vector& Resul } else { - LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlphaMapFile + " in function FEResourceManager::createMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].AlphaMapFile + " in function FEResourceManager::CreateMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); } } if (LoadedTextures.find(OBJLoader.LoadedObjects[i]->MaterialRecords[0].DisplacementMapFile) == LoadedTextures.end() && !OBJLoader.LoadedObjects[i]->MaterialRecords[0].DisplacementMapFile.empty()) { - std::vector LoadedObjects = ImportAsset(OBJLoader.LoadedObjects[i]->MaterialRecords[0].DisplacementMapFile.c_str()); - if (!LoadedObjects.empty() && LoadedObjects[0] != nullptr) + FETexture* LoadedTexture = ImportTexture(OBJLoader.LoadedObjects[i]->MaterialRecords[0].DisplacementMapFile.c_str()); + if (LoadedTexture != nullptr) { - Texture = reinterpret_cast(LoadedObjects[0]); - LoadedTextures[OBJLoader.LoadedObjects[i]->MaterialRecords[0].DisplacementMapFile] = true; Name = FILE_SYSTEM.GetFileName(OBJLoader.LoadedObjects[i]->MaterialRecords[0].DisplacementMapFile.c_str()); - ResultArray.push_back(Texture); + ResultArray.push_back(LoadedTexture); ResultArray.back()->SetName(Name); if (Material == nullptr) @@ -3569,22 +2837,20 @@ void FEResourceManager::CreateMaterialsFromOBJData(std::vector& Resul } else { - LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].DisplacementMapFile + " in function FEResourceManager::createMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].DisplacementMapFile + " in function FEResourceManager::CreateMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); } } if (LoadedTextures.find(OBJLoader.LoadedObjects[i]->MaterialRecords[0].StencilDecalMapFile) == LoadedTextures.end() && !OBJLoader.LoadedObjects[i]->MaterialRecords[0].StencilDecalMapFile.empty()) { - std::vector LoadedObjects = ImportAsset(OBJLoader.LoadedObjects[i]->MaterialRecords[0].StencilDecalMapFile.c_str()); - if (!LoadedObjects.empty() && LoadedObjects[0] != nullptr) + FETexture* LoadedTexture = ImportTexture(OBJLoader.LoadedObjects[i]->MaterialRecords[0].StencilDecalMapFile.c_str()); + if (LoadedTexture != nullptr) { - Texture = reinterpret_cast(LoadedObjects[0]); - LoadedTextures[OBJLoader.LoadedObjects[i]->MaterialRecords[0].StencilDecalMapFile] = true; Name = FILE_SYSTEM.GetFileName(OBJLoader.LoadedObjects[i]->MaterialRecords[0].StencilDecalMapFile.c_str()); - ResultArray.push_back(Texture); + ResultArray.push_back(LoadedTexture); ResultArray.back()->SetName(Name); if (Material == nullptr) @@ -3592,7 +2858,7 @@ void FEResourceManager::CreateMaterialsFromOBJData(std::vector& Resul } else { - LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].StencilDecalMapFile + " in function FEResourceManager::createMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add(std::string("can't load texture: ") + OBJLoader.LoadedObjects[i]->MaterialRecords[0].StencilDecalMapFile + " in function FEResourceManager::CreateMaterialsFromOBJData.", "FE_LOG_LOADING", FE_LOG_ERROR); } } @@ -3607,60 +2873,37 @@ void FEResourceManager::CreateMaterialsFromOBJData(std::vector& Resul } } -std::vector FEResourceManager::ImportAsset(const char* FileName) +FETexture* FEResourceManager::ImportTexture(const char* FileName) { - std::vector Result; + FETexture* Result = nullptr; + if (FileName == nullptr) { - LOG.Add("call of FEResourceManager::importAsset with nullptr fileName", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("call of FEResourceManager::ImportTexture with nullptr FileName", "FE_LOG_LOADING", FE_LOG_ERROR); return Result; } - if (!FILE_SYSTEM.CheckFile(FileName)) + if (!FILE_SYSTEM.DoesFileExist(FileName)) { - LOG.Add("Can't locate file: " + std::string(FileName) + " in FEResourceManager::importAsset", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("Can't locate file: " + std::string(FileName) + " in FEResourceManager::ImportTexture", "FE_LOG_LOADING", FE_LOG_ERROR); return Result; } std::string FileExtention = FILE_SYSTEM.GetFileExtension(FileName); - // To lower case std::transform(FileExtention.begin(), FileExtention.end(), FileExtention.begin(), [](const unsigned char C) { return std::tolower(C); }); if (FileExtention == ".png") { - FETexture* TempTexture = LoadPNGTexture(FileName); - if (TempTexture != nullptr) - { - Result.push_back(TempTexture); - return Result; - } + Result = LoadPNGTexture(FileName); } else if (FileExtention == ".jpg") { - FETexture* TempTexture = LoadJPGTexture(FileName); - if (TempTexture != nullptr) - { - Result.push_back(TempTexture); - return Result; - } + Result = LoadJPGTexture(FileName); + } else if (FileExtention == ".bmp") { - FETexture* TempTexture = LoadBMPTexture(FileName); - if (TempTexture != nullptr) - { - Result.push_back(TempTexture); - return Result; - } - } - else if (FileExtention == ".obj") - { - Result = ImportOBJ(FileName,true); - return Result; - } - else if (FileExtention == ".gltf") - { - return LoadGLTF(FileName); + Result = LoadBMPTexture(FileName); } return Result; @@ -3670,13 +2913,13 @@ FETexture* FEResourceManager::CreateTextureWithTransparency(FETexture* OriginalT { if (OriginalTexture == nullptr || MaskTexture == nullptr) { - LOG.Add("call of FEResourceManager::createTextureWithTransparency with nullptr argument(s)", "FE_LOG_GENERAL", FE_LOG_ERROR); + LOG.Add("call of FEResourceManager::CreateTextureWithTransparency with nullptr argument(s)", "FE_LOG_GENERAL", FE_LOG_ERROR); return nullptr; } if (OriginalTexture->GetWidth() != MaskTexture->GetWidth() || OriginalTexture->GetHeight() != MaskTexture->GetHeight()) { - LOG.Add("originalTexture and maskTexture dimensions mismatch in FEResourceManager::createTextureWithTransparency", "FE_LOG_GENERAL", FE_LOG_ERROR); + LOG.Add("OriginalTexture and MaskTexture dimensions mismatch in FEResourceManager::CreateTextureWithTransparency", "FE_LOG_GENERAL", FE_LOG_ERROR); return nullptr; } @@ -3700,7 +2943,8 @@ FETexture* FEResourceManager::CreateTextureWithTransparency(FETexture* OriginalT if (Result->MipEnabled) { FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); - FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); // to-do: fix this + // TO-DO: make it configurable. + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); } @@ -3728,175 +2972,20 @@ FETexture* FEResourceManager::CreateTextureWithTransparency(FETexture* OriginalT return Result; } -std::vector FEResourceManager::LoadGLTF(const char* FileName) -{ - std::vector Result; - if (!FILE_SYSTEM.CheckFile(FileName)) - { - LOG.Add("call of FEResourceManager::LoadGLTF can't locate file: " + std::string(FileName), "FE_LOG_LOADING", FE_LOG_ERROR); - return Result; - } - - FEGLTFLoader& GLTF = FEGLTFLoader::getInstance(); - GLTF.Load(FileName); - - std::unordered_map AlreadyLoadedTextures; - std::unordered_map TextureMap; - for (size_t i = 0; i < GLTF.Textures.size(); i++) - { - if (AlreadyLoadedTextures.find(GLTF.Textures[i]) != AlreadyLoadedTextures.end()) - { - TextureMap[static_cast(TextureMap.size())] = AlreadyLoadedTextures[GLTF.Textures[i]]; - continue; - } - - if (!FILE_SYSTEM.CheckFile(GLTF.Textures[i].c_str())) - { - TextureMap[static_cast(TextureMap.size())] = nullptr; - continue; - } - - std::vector TempResult = ImportAsset(GLTF.Textures[i].c_str()); - if (!TempResult.empty()) - { - TextureMap[static_cast(TextureMap.size())] = reinterpret_cast(TempResult[0]); - AlreadyLoadedTextures[GLTF.Textures[i]] = reinterpret_cast(TempResult[0]); - Result.push_back(TempResult[0]); - } - } - - std::unordered_map MaterialsMap; - for (size_t i = 0; i < GLTF.Materials.size(); i++) - { - FEMaterial* NewMaterial = CreateMaterial(GLTF.Materials[i].Name); - MaterialsMap[static_cast(i)] = NewMaterial; - NewMaterial->Shader = GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/); - - if (TextureMap.find(GLTF.Materials[i].BaseColorTexture.Index) != TextureMap.end() && TextureMap[GLTF.Materials[i].BaseColorTexture.Index] != nullptr) - { - NewMaterial->AddTexture(TextureMap[GLTF.Materials[i].BaseColorTexture.Index]); - NewMaterial->SetAlbedoMap(TextureMap[GLTF.Materials[i].BaseColorTexture.Index]); - } - else if (GLTF.Materials[i].BaseColor[0] != -1) - { - NewMaterial->BaseColor = GLTF.Materials[i].BaseColor; - } - - if (TextureMap.find(GLTF.Materials[i].MetallicRoughnessTexture.Index) != TextureMap.end() && TextureMap[GLTF.Materials[i].MetallicRoughnessTexture.Index] != nullptr) - { - /* - https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/Specification.adoc#reference-material - The textures for metalness and roughness properties are packed together in a single texture called metallicRoughnessTexture. - Its green channel contains roughness values and its blue channel contains metalness values. - */ - NewMaterial->AddTexture(TextureMap[GLTF.Materials[i].MetallicRoughnessTexture.Index]); - NewMaterial->SetRoughnessMap(TextureMap[GLTF.Materials[i].MetallicRoughnessTexture.Index], 1, 0); - NewMaterial->SetMetalnessMap(TextureMap[GLTF.Materials[i].MetallicRoughnessTexture.Index], 2, 0); - } - - if (TextureMap.find(GLTF.Materials[i].NormalTexture.Index) != TextureMap.end() && TextureMap[GLTF.Materials[i].NormalTexture.Index] != nullptr) - { - NewMaterial->AddTexture(TextureMap[GLTF.Materials[i].NormalTexture.Index]); - NewMaterial->SetNormalMap(TextureMap[GLTF.Materials[i].NormalTexture.Index]); - } - - if (TextureMap.find(GLTF.Materials[i].OcclusionTexture.Index) != TextureMap.end() && TextureMap[GLTF.Materials[i].OcclusionTexture.Index] != nullptr) - { - NewMaterial->AddTexture(TextureMap[GLTF.Materials[i].OcclusionTexture.Index]); - NewMaterial->SetAOMap(TextureMap[GLTF.Materials[i].OcclusionTexture.Index]); - } - - Result.push_back(NewMaterial); - } - - std::unordered_map PrefabMap; - for (size_t i = 0; i < GLTF.Meshes.size(); i++) - { - PrefabMap[static_cast(i)] = nullptr; - - if (!GLTF.Meshes[i].Primitives[0].RawData.Indices.empty()) - { - if (GLTF.Meshes[i].Primitives[0].Material != -1) - { - int UVIndex = 0; - UVIndex = GLTF.Materials[GLTF.Meshes[i].Primitives[0].Material].BaseColorTexture.TexCoord; - if (GLTF.Meshes[i].Primitives[0].RawData.UVs.size() <= UVIndex) - UVIndex = 0; - } - - Result.push_back(RawDataToMesh(GLTF.Meshes[i].Primitives[0].RawData.Positions, - GLTF.Meshes[i].Primitives[0].RawData.Normals, - GLTF.Meshes[i].Primitives[0].RawData.Tangents, - GLTF.Meshes[i].Primitives[0].RawData.UVs[0/*UVIndex*/], - GLTF.Meshes[i].Primitives[0].RawData.Indices, - GLTF.Meshes[i].Name)); - - if (GLTF.Meshes[i].Primitives[0].Material != -1) - { - FEGameModel* NewGameModel = CreateGameModel(reinterpret_cast(Result.back()), MaterialsMap[GLTF.Meshes[i].Primitives[0].Material]); - NewGameModel->SetName(GLTF.Meshes[i].Name + "_GameModel"); - Result.push_back(NewGameModel); - - FEPrefab* NewPrefab = CreatePrefab(NewGameModel); - NewPrefab->SetName(GLTF.Meshes[i].Name + "_Prefab"); - PrefabMap[static_cast(i)] = NewPrefab; - Result.push_back(NewPrefab); - } - } - } - - for (size_t i = 0; i < GLTF.Nodes.size(); i++) - { - int PrefabIndex = -1; - PrefabIndex = GLTF.Nodes[i].Mesh; - - if (PrefabIndex != -1) - { - if (PrefabMap.find(PrefabIndex) == PrefabMap.end()) - { - LOG.Add("PrefabMap does not contain PrefabIndex in FEResourceManager::LoadGLTF", "FE_LOG_LOADING", FE_LOG_ERROR); - continue; - } - - if (PrefabMap[PrefabIndex] == nullptr) - { - LOG.Add("PrefabMap[PrefabIndex] is nullptr in FEResourceManager::LoadGLTF", "FE_LOG_LOADING", FE_LOG_ERROR); - continue; - } - - FEEntity* NewEntity = CreateEntity(PrefabMap[PrefabIndex], GLTF.Nodes[i].Name); - NewEntity->Transform.SetPosition(GLTF.Nodes[i].Translation); - NewEntity->Transform.RotateByQuaternion(GLTF.Nodes[i].Rotation); - NewEntity->Transform.SetScale(GLTF.Nodes[i].Scale); - - Result.push_back(NewEntity); - } - } - - return Result; -} - -std::vector FEResourceManager::GetPrefabList() +std::vector FEResourceManager::GetPrefabIDList() { FE_MAP_TO_STR_VECTOR(Prefabs) } -std::vector FEResourceManager::GetStandardPrefabList() +std::vector FEResourceManager::GetEnginePrivatePrefabIDList() { - FE_MAP_TO_STR_VECTOR(StandardPrefabs) + return GetResourceIDListByTag(Prefabs, ENGINE_RESOURCE_TAG); } FEPrefab* FEResourceManager::GetPrefab(const std::string ID) { if (Prefabs.find(ID) == Prefabs.end()) - { - if (StandardPrefabs.find(ID) != StandardPrefabs.end()) - { - return StandardPrefabs[ID]; - } - return nullptr; - } return Prefabs[ID]; } @@ -3905,37 +2994,26 @@ std::vector FEResourceManager::GetPrefabByName(const std::string Name { std::vector Result; - auto it = Prefabs.begin(); - while (it != Prefabs.end()) - { - if (it->second->GetName() == Name) - { - Result.push_back(it->second); - } - - it++; - } - - it = StandardPrefabs.begin(); - while (it != StandardPrefabs.end()) + auto PrefabsIterator = Prefabs.begin(); + while (PrefabsIterator != Prefabs.end()) { - if (it->second->GetName() == Name) + if (PrefabsIterator->second->GetName() == Name) { - Result.push_back(it->second); + Result.push_back(PrefabsIterator->second); } - it++; + PrefabsIterator++; } return Result; } -FEPrefab* FEResourceManager::CreatePrefab(FEGameModel* GameModel, std::string Name, const std::string ForceObjectID) +FEPrefab* FEResourceManager::CreatePrefab(std::string Name, const std::string ForceObjectID, FEScene* SceneDescription) { if (Name.empty()) - Name = "unnamedPrefab"; + Name = "Unnamed prefab"; - FEPrefab* NewPrefab = new FEPrefab(); + FEPrefab* NewPrefab = new FEPrefab(Name, SceneDescription == nullptr); if (!ForceObjectID.empty()) { Prefabs[ForceObjectID] = NewPrefab; @@ -3947,33 +3025,56 @@ FEPrefab* FEResourceManager::CreatePrefab(FEGameModel* GameModel, std::string Na } Prefabs[NewPrefab->ID]->SetName(Name); - if (GameModel != nullptr) + if (SceneDescription != nullptr) { - Prefabs[NewPrefab->ID]->Components.push_back(new FEPrefabComponent()); - Prefabs[NewPrefab->ID]->Components.back()->GameModel = GameModel; + SceneDescription->SetFlag(FESceneFlag::PrefabDescription, true); + Prefabs[NewPrefab->ID]->Scene = SceneDescription; } - + return Prefabs[NewPrefab->ID]; } -bool FEResourceManager::MakePrefabStandard(FEPrefab* Prefab) +Json::Value FEResourceManager::SavePrefabToJSON(FEPrefab* Prefab) { - if (Prefab == nullptr) + Json::Value Root; + + Root["FEObjectData"] = RESOURCE_MANAGER.SaveFEObjectPart(Prefab); + if (Prefab->GetScene() == nullptr) { - LOG.Add("prefab is nullptr in function FEResourceManager::makePrefabStandard.", "FE_LOG_GENERAL", FE_LOG_ERROR); - return false; + LOG.Add("FEResourceManager::SavePrefabToJSON: Prefab scene is nullptr!", "FE_LOG_LOADING", FE_LOG_ERROR); + Root["SceneID"] = ""; + return Root; } - if (StandardPrefabs.find(Prefab->GetObjectID()) == StandardPrefabs.end()) + Root["SceneID"] = Prefab->GetScene()->GetObjectID(); + return Root; +} + +FEPrefab* FEResourceManager::LoadPrefabFromJSON(Json::Value& Root) +{ + FEObjectLoadedData LoadedObjectData = RESOURCE_MANAGER.LoadFEObjectPart(Root["FEObjectData"]); + + std::string SceneID; + if (Root.isMember("Scene")) + { + SceneID = Root["Scene"]["ID"].asCString(); + } + else { - if (Prefabs.find(Prefab->GetObjectID()) != Prefabs.end()) - Prefabs.erase(Prefab->GetObjectID()); - StandardPrefabs[Prefab->GetObjectID()] = Prefab; + SceneID = Root["SceneID"].asCString(); + } - return true; + FEScene* Scene = SCENE_MANAGER.GetScene(SceneID); + if (Scene == nullptr) + { + LOG.Add("FEResourceManager::LoadPrefabFromJSON: Prefab scene is missing!", "FE_LOG_LOADING", FE_LOG_ERROR); + return nullptr; } - return false; + FEPrefab* NewPrefab = RESOURCE_MANAGER.CreatePrefab(LoadedObjectData.Name, LoadedObjectData.ID, Scene); + RESOURCE_MANAGER.SetTag(NewPrefab, LoadedObjectData.Tag); + + return NewPrefab; } void FEResourceManager::DeletePrefab(const FEPrefab* Prefab) @@ -3982,13 +3083,6 @@ void FEResourceManager::DeletePrefab(const FEPrefab* Prefab) delete Prefab; } -void FEResourceManager::LoadStandardPrefabs() -{ - FEPrefab* NewPrefab = new FEPrefab(GetGameModel("67251E393508013ZV579315F"/*"standardGameModel"*/), "standardPrefab"); - NewPrefab->SetID("4575527C773848040760656F"); - MakePrefabStandard(NewPrefab); -} - void FEResourceManager::AddColorToFEMeshVertices(FEMesh* Mesh, float* Colors, int ColorSize) { if (Mesh == nullptr) @@ -4007,4 +3101,985 @@ void FEResourceManager::AddColorToFEMeshVertices(FEMesh* Mesh, float* Colors, in FE_GL_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(float) * Mesh->ColorCount, Colors, GL_STATIC_DRAW)); FE_GL_ERROR(glVertexAttribPointer(1/*FE_COLOR*/, 3, GL_FLOAT, false, 0, 0)); FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, 0)); +} + +Json::Value FEResourceManager::SaveFEObjectPart(FEObject* Object) +{ + Json::Value Root; + Root["ID"] = Object->GetObjectID(); + Root["Tag"] = Object->GetTag(); + Root["Name"] = Object->GetName(); + Root["Type"] = Object->GetType(); + + return Root; +} + +FEObjectLoadedData FEResourceManager::LoadFEObjectPart(Json::Value Root) +{ + FEObjectLoadedData Result; + + if (Root.isMember("ID") && Root["ID"].isString()) + Result.ID = Root["ID"].asString(); + + if (Root.isMember("Tag") && Root["Tag"].isString()) + Result.Tag = Root["Tag"].asString(); + + if (Root.isMember("Name") && Root["Name"].isString()) + Result.Name = Root["Name"].asString(); + + if (Root.isMember("Type") && Root["Type"].isInt()) + Result.Type = FE_OBJECT_TYPE(Root["Type"].asInt()); + + return Result; +} + +std::vector FEResourceManager::GetTagsThatWillPreventDeletion() +{ + std::vector Result; + for (size_t i = 0; i < TagsThatWillPreventDeletion.size(); i++) + Result.push_back(TagsThatWillPreventDeletion[i]); + + return Result; +} + +void FEResourceManager::AddTagThatWillPreventDeletion(std::string Tag) +{ + if (std::find(TagsThatWillPreventDeletion.begin(), TagsThatWillPreventDeletion.end(), Tag) == TagsThatWillPreventDeletion.end()) + TagsThatWillPreventDeletion.push_back(Tag); +} + +void FEResourceManager::RemoveTagThatWillPreventDeletion(std::string Tag) +{ + if (Tag == ENGINE_RESOURCE_TAG) + return; + + for (size_t i = 0; i < TagsThatWillPreventDeletion.size(); i++) + { + if (TagsThatWillPreventDeletion[i] == Tag) + { + TagsThatWillPreventDeletion.erase(TagsThatWillPreventDeletion.begin() + i); + return; + } + } +} + +std::vector FEResourceManager::GetNativeScriptModuleIDList() +{ + FE_MAP_TO_STR_VECTOR(NativeScriptModules); +} + +std::vector FEResourceManager::GetEnginePrivateNativeScriptModuleIDList() +{ + return GetResourceIDListByTag(NativeScriptModules, ENGINE_RESOURCE_TAG); +} + +FENativeScriptModule* FEResourceManager::GetNativeScriptModule(std::string ID) +{ + if (NativeScriptModules.find(ID) == NativeScriptModules.end()) + return nullptr; + + return NativeScriptModules[ID]; +} + +std::vector FEResourceManager::GetNativeScriptModuleByName(std::string Name) +{ + std::vector Result; + + auto NativeScriptModulesIterator = NativeScriptModules.begin(); + while (NativeScriptModulesIterator != NativeScriptModules.end()) + { + if (NativeScriptModulesIterator->second->GetName() == Name) + { + Result.push_back(NativeScriptModulesIterator->second); + } + + NativeScriptModulesIterator++; + } + + return Result; +} + +std::string FEResourceManager::ReadDLLModuleID(std::string DLLFilePath) +{ + if (DLLFilePath.empty()) + { + LOG.Add("call of FEResourceManager::ReadDLLModuleID with empty DLLFilePath", "FE_LOG_GENERAL", FE_LOG_ERROR); + return ""; + } + + if (!FILE_SYSTEM.DoesFileExist(DLLFilePath)) + { + LOG.Add("can't locate file: " + DLLFilePath + " in FEResourceManager::ReadDLLModuleID", "FE_LOG_LOADING", FE_LOG_ERROR); + return ""; + } + + HMODULE DLLHandle = LoadLibraryA(DLLFilePath.c_str()); + if (!DLLHandle) + { + LOG.Add("FEResourceManager::ReadDLLModuleID failed to load DLL: " + DLLFilePath, "FE_LOG_LOADING", FE_LOG_ERROR); + return ""; + } + + typedef char* (*Get_ModuleID_Function)(void); + Get_ModuleID_Function GetModuleID = (Get_ModuleID_Function)GetProcAddress(DLLHandle, "GetModuleID"); + if (!GetModuleID) + { + LOG.Add("FEResourceManager::ReadDLLModuleID failed to get GetModuleID function from DLL: " + DLLFilePath, "FE_LOG_LOADING", FE_LOG_ERROR); + return ""; + } + + std::string DLLModuleID = GetModuleID(); + if (DLLModuleID.empty() || DLLModuleID.size() != 24) + { + LOG.Add("FEResourceManager::ReadDLLModuleID failed to get proper DLLModuleID from DLL: " + DLLFilePath, "FE_LOG_LOADING", FE_LOG_ERROR); + return ""; + } + + FreeLibrary(DLLHandle); + return DLLModuleID; +} + +FENativeScriptModule* FEResourceManager::CreateNativeScriptModule(std::string Name, std::string ForceObjectID) +{ + if (Name.empty()) + Name = "Unnamed NativeScriptModule"; + + FENativeScriptModule* NewNativeScriptModule = new FENativeScriptModule(); + if (!ForceObjectID.empty()) + { + NativeScriptModules[ForceObjectID] = NewNativeScriptModule; + NativeScriptModules[ForceObjectID]->SetID(ForceObjectID); + } + else + { + NativeScriptModules[NewNativeScriptModule->ID] = NewNativeScriptModule; + } + + NativeScriptModules[NewNativeScriptModule->ID]->SetName(Name); + return NativeScriptModules[NewNativeScriptModule->ID]; +} + +FENativeScriptModule* FEResourceManager::CreateNativeScriptModule(std::string DebugDLLFilePath, std::string DebugPDBFilePath, std::string ReleaseDLLFilePath, std::vector ScriptFiles, std::string Name, std::string ForceObjectID) +{ + if (DebugDLLFilePath.empty()) + { + LOG.Add("call of FEResourceManager::CreateNativeScriptModule with empty DebugDLLFilePath", "FE_LOG_GENERAL", FE_LOG_ERROR); + return nullptr; + } + + if (DebugPDBFilePath.empty()) + { + LOG.Add("call of FEResourceManager::CreateNativeScriptModule with empty DebugPDBFilePath", "FE_LOG_GENERAL", FE_LOG_ERROR); + return nullptr; + } + + if (ReleaseDLLFilePath.empty()) + { + LOG.Add("call of FEResourceManager::CreateNativeScriptModule with empty ReleaseDLLFilePath", "FE_LOG_GENERAL", FE_LOG_ERROR); + return nullptr; + } + + if (Name.empty()) + Name = "Unnamed NativeScriptModule"; + + // First we need to check if files are valid. + if (!FILE_SYSTEM.DoesFileExist(DebugDLLFilePath)) + { + LOG.Add("can't locate file: " + DebugDLLFilePath + " in FEResourceManager::CreateNativeScriptModule", "FE_LOG_LOADING", FE_LOG_ERROR); + return nullptr; + } + + if (!FILE_SYSTEM.DoesFileExist(DebugPDBFilePath)) + { + LOG.Add("can't locate file: " + DebugPDBFilePath + " in FEResourceManager::CreateNativeScriptModule", "FE_LOG_LOADING", FE_LOG_ERROR); + return nullptr; + } + + if (!FILE_SYSTEM.DoesFileExist(ReleaseDLLFilePath)) + { + LOG.Add("can't locate file: " + ReleaseDLLFilePath + " in FEResourceManager::CreateNativeScriptModule", "FE_LOG_LOADING", FE_LOG_ERROR); + return nullptr; + } + + // We also need to retrieve DLL module ID from debug and release DLLs. + std::string DebugDLLModuleID = ReadDLLModuleID(DebugDLLFilePath); + if (DebugDLLModuleID.empty()) + { + LOG.Add("FEResourceManager::CreateNativeScriptModule failed to get DLLModuleID from DLL: " + DebugDLLFilePath, "FE_LOG_LOADING", FE_LOG_ERROR); + return nullptr; + } + + std::string ReleaseDLLModuleID = ReadDLLModuleID(DebugDLLFilePath); + if (ReleaseDLLModuleID.empty()) + { + LOG.Add("FEResourceManager::CreateNativeScriptModule failed to get DLLModuleID from DLL: " + DebugDLLFilePath, "FE_LOG_LOADING", FE_LOG_ERROR); + return nullptr; + } + + if (DebugDLLModuleID != ReleaseDLLModuleID) + { + LOG.Add("FEResourceManager::CreateNativeScriptModule DLLModuleID mismatch between debug and release DLLs", "FE_LOG_LOADING", FE_LOG_ERROR); + return nullptr; + } + + FENativeScriptModule* NewNativeScriptModule = new FENativeScriptModule(DebugDLLFilePath, DebugPDBFilePath, ReleaseDLLFilePath, ScriptFiles); + if (!ForceObjectID.empty()) + { + NativeScriptModules[ForceObjectID] = NewNativeScriptModule; + NativeScriptModules[ForceObjectID]->SetID(ForceObjectID); + } + else + { + NativeScriptModules[NewNativeScriptModule->ID] = NewNativeScriptModule; + } + + NativeScriptModules[NewNativeScriptModule->ID]->SetName(Name); + return NativeScriptModules[NewNativeScriptModule->ID]; +} + +FENativeScriptModule* FEResourceManager::LoadFENativeScriptModule(std::string FileName) +{ + if (FileName.empty()) + { + LOG.Add("call of FEResourceManager::LoadFENativeScriptModule with empty FileName", "FE_LOG_LOADING", FE_LOG_ERROR); + return nullptr; + } + + if (!FILE_SYSTEM.DoesFileExist(FileName)) + { + LOG.Add("can't locate file: " + FileName + " in FEResourceManager::LoadFENativeScriptModule", "FE_LOG_LOADING", FE_LOG_ERROR); + return nullptr; + } + + std::fstream File; + File.open(FileName, std::ios::in | std::ios::binary); + if (!File.is_open()) + { + LOG.Add("can't open file: " + FileName + " in FEResourceManager::LoadFENativeScriptModule", "FE_LOG_LOADING", FE_LOG_ERROR); + return nullptr; + } + + float Version; + File.read((char*)&Version, sizeof(float)); + if (Version != FE_NATIVE_SCRIPT_MODULE_VERSION) + { + LOG.Add("version mismatch in FEResourceManager::LoadFENativeScriptModule", "FE_LOG_LOADING", FE_LOG_ERROR); + File.close(); + return nullptr; + } + + FENativeScriptModule* NewNativeScriptModule = new FENativeScriptModule(); + FEObjectLoadedData ObjectData = OBJECT_MANAGER.LoadFEObjectPart(File); + NewNativeScriptModule->SetID(ObjectData.ID); + NewNativeScriptModule->SetTag(ObjectData.Tag); + NewNativeScriptModule->SetName(ObjectData.Name); + NewNativeScriptModule->SetType(ObjectData.Type); + + // Load DebugDLLAssetID. + size_t DebugDllAssetIDSize = 0; + File.read((char*)&DebugDllAssetIDSize, sizeof(size_t)); + char* DebugDllAssetID = new char[DebugDllAssetIDSize]; + File.read(DebugDllAssetID, DebugDllAssetIDSize); + NewNativeScriptModule->DebugDLLAssetID = std::string(DebugDllAssetID, DebugDllAssetIDSize); + delete[] DebugDllAssetID; + + // Load DebugPDBAssetID. + size_t DebugPdbAssetIDSize = 0; + File.read((char*)&DebugPdbAssetIDSize, sizeof(size_t)); + char* DebugPdbAssetID = new char[DebugPdbAssetIDSize]; + File.read(DebugPdbAssetID, DebugPdbAssetIDSize); + NewNativeScriptModule->DebugPDBAssetID = std::string(DebugPdbAssetID, DebugPdbAssetIDSize); + delete[] DebugPdbAssetID; + + // Load ReleaseDLLAssetID. + size_t ReleaseDllAssetIDSize = 0; + File.read((char*)&ReleaseDllAssetIDSize, sizeof(size_t)); + char* ReleaseDllAssetID = new char[ReleaseDllAssetIDSize]; + File.read(ReleaseDllAssetID, ReleaseDllAssetIDSize); + NewNativeScriptModule->ReleaseDLLAssetID = std::string(ReleaseDllAssetID, ReleaseDllAssetIDSize); + delete[] ReleaseDllAssetID; + + // Load CMakeFileAssetID. + size_t CMakeFileAssetIDSize = 0; + File.read((char*)&CMakeFileAssetIDSize, sizeof(size_t)); + char* CMakeFileAssetID = new char[CMakeFileAssetIDSize]; + File.read(CMakeFileAssetID, CMakeFileAssetIDSize); + NewNativeScriptModule->CMakeFileAssetID = std::string(CMakeFileAssetID, CMakeFileAssetIDSize); + delete[] CMakeFileAssetID; + + // Load ScriptAssetIDs. + size_t ScriptAssetIDsSize = 0; + File.read((char*)&ScriptAssetIDsSize, sizeof(size_t)); + for (size_t i = 0; i < ScriptAssetIDsSize; i++) + { + size_t ScriptAssetIDSize = 0; + File.read((char*)&ScriptAssetIDSize, sizeof(size_t)); + char* ScriptAssetID = new char[ScriptAssetIDSize]; + File.read(ScriptAssetID, ScriptAssetIDSize); + NewNativeScriptModule->ScriptAssetIDs.push_back(std::string(ScriptAssetID, ScriptAssetIDSize)); + delete[] ScriptAssetID; + } + + // Load ScriptAssetPackage. + size_t PackageFullCopySize = 0; + File.read((char*)&PackageFullCopySize, sizeof(size_t)); + unsigned char* PackageFullCopy = new unsigned char[PackageFullCopySize]; + File.read((char*)PackageFullCopy, PackageFullCopySize); + NewNativeScriptModule->ScriptAssetPackage = new FEAssetPackage(); + NewNativeScriptModule->ScriptAssetPackage->LoadFromMemory(PackageFullCopy, PackageFullCopySize); + delete[] PackageFullCopy; + + // Load project data. + size_t ProjectPackageFullCopySize = 0; + File.read((char*)&ProjectPackageFullCopySize, sizeof(size_t)); + if (ProjectPackageFullCopySize > 0) + { + unsigned char* ProjectPackageFullCopy = new unsigned char[ProjectPackageFullCopySize]; + File.read((char*)ProjectPackageFullCopy, ProjectPackageFullCopySize); + + FEAssetPackage* ProjectData = new FEAssetPackage(); + ProjectData->LoadFromMemory(ProjectPackageFullCopy, ProjectPackageFullCopySize); + NewNativeScriptModule->Project->Initialize(ProjectData); + delete[] ProjectPackageFullCopy; + } + + File.close(); + + NativeScriptModules[NewNativeScriptModule->ID] = NewNativeScriptModule; + return NativeScriptModules[NewNativeScriptModule->ID]; +} + +void FEResourceManager::SaveFENativeScriptModule(FENativeScriptModule* NativeScriptModule, std::string FileName) +{ + if (NativeScriptModule == nullptr) + { + LOG.Add("call of FEResourceManager::SaveFENativeScriptModule with nullptr NativeScriptModule", "FE_LOG_SAVING", FE_LOG_ERROR); + return; + } + + if (FileName.empty()) + { + LOG.Add("call of FEResourceManager::SaveFENativeScriptModule with empty FileName", "FE_LOG_SAVING", FE_LOG_ERROR); + return; + } + + std::fstream File; + File.open(FileName, std::ios::out | std::ios::binary); + if (!File.is_open()) + { + LOG.Add("can't open file: " + FileName + " in FEResourceManager::SaveFENativeScriptModule", "FE_LOG_SAVING", FE_LOG_ERROR); + return; + } + + // Version of FENativeScriptModule file. + float Version = FE_NATIVE_SCRIPT_MODULE_VERSION; + File.write((char*)&Version, sizeof(float)); + + OBJECT_MANAGER.SaveFEObjectPart(File, NativeScriptModule); + + // Save DebugDLLAssetID. + size_t DebugDllAssetIDSize = NativeScriptModule->DebugDLLAssetID.size(); + File.write((char*)&DebugDllAssetIDSize, sizeof(size_t)); + File.write(NativeScriptModule->DebugDLLAssetID.c_str(), DebugDllAssetIDSize); + + // Save DebugPDBAssetID. + size_t DebugPdbAssetIDSize = NativeScriptModule->DebugPDBAssetID.size(); + File.write((char*)&DebugPdbAssetIDSize, sizeof(size_t)); + File.write(NativeScriptModule->DebugPDBAssetID.c_str(), DebugPdbAssetIDSize); + + // Save ReleaseDLLAssetID. + size_t ReleaseDllAssetIDSize = NativeScriptModule->ReleaseDLLAssetID.size(); + File.write((char*)&ReleaseDllAssetIDSize, sizeof(size_t)); + File.write(NativeScriptModule->ReleaseDLLAssetID.c_str(), ReleaseDllAssetIDSize); + + // Save CMakeFileAssetID. + size_t CMakeFileAssetIDSize = NativeScriptModule->CMakeFileAssetID.size(); + File.write((char*)&CMakeFileAssetIDSize, sizeof(size_t)); + File.write(NativeScriptModule->CMakeFileAssetID.c_str(), CMakeFileAssetIDSize); + + // Save ScriptAssetIDs. + size_t ScriptAssetIDsSize = NativeScriptModule->ScriptAssetIDs.size(); + File.write((char*)&ScriptAssetIDsSize, sizeof(size_t)); + for (size_t i = 0; i < NativeScriptModule->ScriptAssetIDs.size(); i++) + { + size_t ScriptAssetIDSize = NativeScriptModule->ScriptAssetIDs[i].size(); + File.write((char*)&ScriptAssetIDSize, sizeof(size_t)); + File.write(NativeScriptModule->ScriptAssetIDs[i].c_str(), ScriptAssetIDSize); + } + + // Save ScriptAssetPackage. + size_t PackageFullCopySize = 0; + unsigned char* PackageFullCopy = NativeScriptModule->ScriptAssetPackage->ExportAsRawData(PackageFullCopySize); + File.write((char*)&PackageFullCopySize, sizeof(size_t)); + File.write((char*)PackageFullCopy, PackageFullCopySize); + + // Saveing project data. + size_t ProjectPackageFullCopySize = 0; + if (NativeScriptModule->Project != nullptr) + { + NativeScriptModule->Project->UpdateDataToRecoverVSProject(); + if (NativeScriptModule->Project->DataToRecoverVSProject == nullptr) + { + LOG.Add("NativeScriptModule->Project->DataToRecoverVSProject is nullptr in FEResourceManager::SaveFENativeScriptModule", "FE_LOG_SAVING", FE_LOG_WARNING); + File.write((char*)&ProjectPackageFullCopySize, sizeof(size_t)); + } + else + { + unsigned char* ProjectPackageFullCopy = NativeScriptModule->Project->DataToRecoverVSProject->ExportAsRawData(ProjectPackageFullCopySize); + File.write((char*)&ProjectPackageFullCopySize, sizeof(size_t)); + File.write((char*)ProjectPackageFullCopy, ProjectPackageFullCopySize); + } + } + else + { + LOG.Add("NativeScriptModule->Project is nullptr in FEResourceManager::SaveFENativeScriptModule", "FE_LOG_SAVING", FE_LOG_WARNING); + File.write((char*)&ProjectPackageFullCopySize, sizeof(size_t)); + } + + File.close(); +} + +bool FEResourceManager::DeleteNativeScriptModuleInternal(FENativeScriptModule* Module) +{ + if (Module == nullptr) + { + LOG.Add("call of FEResourceManager::DeleteNativeScriptModuleInternal with nullptr Module", "FE_LOG_GENERAL", FE_LOG_ERROR); + return false; + } + + if (Module->GetTag() == ENGINE_RESOURCE_TAG) + { + LOG.Add("can't delete Engine Private NativeScriptModule", "FE_LOG_GENERAL", FE_LOG_ERROR); + return false; + } + + if (NativeScriptModules.find(Module->GetObjectID()) == NativeScriptModules.end()) + { + LOG.Add("can't find Module in NativeScriptModules in FEResourceManager::DeleteNativeScriptModuleInternal", "FE_LOG_GENERAL", FE_LOG_ERROR); + return false; + } + + NativeScriptModules.erase(Module->GetObjectID()); + delete Module; + + return true; +} + +FEAssetPackage* FEResourceManager::CreateEngineHeadersAssetPackage() +{ + FEAssetPackage* EngineHeadersAssetPackage = new FEAssetPackage(); + EngineHeadersAssetPackage->SetName("EngineHeaders"); + if (EngineHeadersAssetPackage == nullptr) + { + LOG.Add("FEResourceManager::CreateEngineHeadersAssetPackage: Error creating asset package", "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return nullptr; + } + + std::string EnginePath = FILE_SYSTEM.GetCurrentWorkingPath() + "/" + std::string(ENGINE_FOLDER) + "/"; + if (!FILE_SYSTEM.DoesDirectoryExist(EnginePath)) + { + LOG.Add("FEResourceManager::CreateEngineHeadersAssetPackage: Engine folder does not exist", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return nullptr; + } + + std::vector AllFiles = FILE_SYSTEM.GetFilesInDirectory(EnginePath, true); + // After having all files in the engine folder, we need to filter out only the header files. + for (size_t i = 0; i < AllFiles.size(); i++) + { + if (AllFiles[i].substr(AllFiles[i].size() - 2) == ".h" || AllFiles[i].substr(AllFiles[i].size() - 4) == ".hpp" || AllFiles[i].substr(AllFiles[i].size() - 4) == ".inl") + { + FEAssetPackageEntryIntializeData EntryData; + // Also since FEAssetPackage does not support folders, we need to save folder structure in the file name. + // But we will erase the engine folder path from the file name. + EntryData.Name = AllFiles[i].substr(EnginePath.size()); + EntryData.Type = "Text"; + EntryData.Tag = ENGINE_RESOURCE_TAG; + EntryData.Comment = "Engine header file"; + + EngineHeadersAssetPackage->ImportAssetFromFile(AllFiles[i], EntryData); + } + } + + return EngineHeadersAssetPackage; +} + +bool FEResourceManager::UnPackEngineHeadersAssetPackage(FEAssetPackage* AssetPackage, std::string Path) +{ + if (AssetPackage == nullptr) + { + LOG.Add("FEResourceManager::UnPackEngineHeadersAssetPackage: Asset package is nullptr", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + if (Path.empty()) + { + LOG.Add("FEResourceManager::UnPackEngineHeadersAssetPackage: Destination path is empty", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + if (!FILE_SYSTEM.DoesDirectoryExist(Path)) + { + LOG.Add("FEResourceManager::UnPackEngineHeadersAssetPackage: Destination path does not exist", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + std::vector AssetPackageContent = AssetPackage->GetEntryList(); + if (AssetPackageContent.empty()) + { + LOG.Add("FEResourceManager::UnPackEngineHeadersAssetPackage: Asset package is empty", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + for (size_t i = 0; i < AssetPackageContent.size(); i++) + { + std::string LocalPath = std::filesystem::path(AssetPackageContent[i].Name).parent_path().string(); + // Since we are not using folders in FEAssetPackage, we need to create all folders in the file path. + // First we need to get chain of folders. + std::vector FolderChain; + try + { + std::filesystem::path Directory(LocalPath); + while (!Directory.string().empty()) + { + if (!FolderChain.empty()) + { + if (FolderChain.back() == Directory.string()) + break; + } + FolderChain.push_back(Directory.string()); + Directory = Directory.parent_path(); + } + + std::reverse(FolderChain.begin(), FolderChain.end()); + } + catch (const std::exception& Exception) + { + LOG.Add("Error in FEResourceManager::UnPackEngineHeadersAssetPackage: " + std::string(Exception.what()), "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + + // Then we will go from the root folder to the last folder and create them if they do not exist. + for (size_t i = 0; i < FolderChain.size(); i++) + { + std::string FinalPath = Path + FolderChain[i]; + if (!FILE_SYSTEM.DoesDirectoryExist(FinalPath)) + { + if (!FILE_SYSTEM.CreateDirectory(FinalPath)) + { + LOG.Add("FEResourceManager::UnPackEngineHeadersAssetPackage: Error creating directory " + FinalPath, "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + } + } + + // Now we are ready to write the file. + if (!AssetPackage->ExportAssetToFile(AssetPackageContent[i].ID, Path + AssetPackageContent[i].Name)) + { + LOG.Add("FEResourceManager::UnPackEngineHeadersAssetPackage: Error exporting asset " + AssetPackageContent[i].ID + " to " + Path + AssetPackageContent[i].Name, "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + } + + return true; +} + +FEAssetPackage* FEResourceManager::CreateEngineSourceFilesAssetPackage() +{ + FEAssetPackage* EngineSourceFilesAssetPackage = new FEAssetPackage(); + EngineSourceFilesAssetPackage->SetName("EngineSourceFiles"); + if (EngineSourceFilesAssetPackage == nullptr) + { + LOG.Add("FEResourceManager::CreateEngineSourceFilesAssetPackage: Error creating asset package", "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return nullptr; + } + + std::string EnginePath = FILE_SYSTEM.GetCurrentWorkingPath() + "/" + std::string(ENGINE_FOLDER) + "/"; + if (!FILE_SYSTEM.DoesDirectoryExist(EnginePath)) + { + LOG.Add("FEResourceManager::CreateEngineSourceFilesAssetPackage: Engine folder does not exist", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return nullptr; + } + + std::vector AllFiles = FILE_SYSTEM.GetFilesInDirectory(EnginePath, true); + // After having all files in the engine folder, we need to filter out only the source files. + for (size_t i = 0; i < AllFiles.size(); i++) + { + if (AllFiles[i].substr(AllFiles[i].size() - 4) == ".cpp" || AllFiles[i].substr(AllFiles[i].size() - 2) == ".c") + { + FEAssetPackageEntryIntializeData EntryData; + // Also since FEAssetPackage does not support folders, we need to save folder structure in the file name. + // But we will erase the engine folder path from the file name. + EntryData.Name = AllFiles[i].substr(EnginePath.size()); + EntryData.Type = "Text"; + EntryData.Tag = ENGINE_RESOURCE_TAG; + EntryData.Comment = "Engine source file"; + + EngineSourceFilesAssetPackage->ImportAssetFromFile(AllFiles[i], EntryData); + } + } + + return EngineSourceFilesAssetPackage; +} + +bool FEResourceManager::UnPackEngineSourceFilesAssetPackage(FEAssetPackage* AssetPackage, std::string Path) +{ + if (AssetPackage == nullptr) + { + LOG.Add("FEResourceManager::UnPackEngineSourceFilesAssetPackage: Asset package is nullptr", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + if (Path.empty()) + { + LOG.Add("FEResourceManager::UnPackEngineSourceFilesAssetPackage: Destination path is empty", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + if (!FILE_SYSTEM.DoesDirectoryExist(Path)) + { + LOG.Add("FEResourceManager::UnPackEngineSourceFilesAssetPackage: Destination path does not exist", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + std::vector AssetPackageContent = AssetPackage->GetEntryList(); + if (AssetPackageContent.empty()) + { + LOG.Add("FEResourceManager::UnPackEngineSourceFilesAssetPackage: Asset package is empty", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + for (size_t i = 0; i < AssetPackageContent.size(); i++) + { + std::string LocalPath = std::filesystem::path(AssetPackageContent[i].Name).parent_path().string(); + // Since we are not using folders in FEAssetPackage, we need to create all folders in the file path. + // First we need to get chain of folders. + std::vector FolderChain; + try + { + std::filesystem::path Directory(LocalPath); + while (!Directory.string().empty()) + { + if (!FolderChain.empty()) + { + if (FolderChain.back() == Directory.string()) + break; + } + FolderChain.push_back(Directory.string()); + Directory = Directory.parent_path(); + } + + std::reverse(FolderChain.begin(), FolderChain.end()); + } + catch (const std::exception& Exception) + { + LOG.Add("Error in FEResourceManager::UnPackEngineSourceFilesAssetPackage: " + std::string(Exception.what()), "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + + // Then we will go from the root folder to the last folder and create them if they do not exist. + for (size_t j = 0; j < FolderChain.size(); j++) + { + std::string FinalPath = Path + FolderChain[j]; + if (!FILE_SYSTEM.DoesDirectoryExist(FinalPath)) + { + if (!FILE_SYSTEM.CreateDirectory(FinalPath)) + { + LOG.Add("FEResourceManager::UnPackEngineSourceFilesAssetPackage: Error creating directory " + FinalPath, "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + } + } + + // Now we are ready to write the file. + if (!AssetPackage->ExportAssetToFile(AssetPackageContent[i].ID, Path + AssetPackageContent[i].Name)) + { + LOG.Add("FEResourceManager::UnPackEngineSourceFilesAssetPackage: Error exporting asset " + AssetPackageContent[i].ID + " to " + Path + AssetPackageContent[i].Name, "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + } + + return true; +} + +FEAssetPackage* FEResourceManager::CreateEngineLIBAssetPackage() +{ + FEAssetPackage* EngineLIBAssetPackage = new FEAssetPackage(); + EngineLIBAssetPackage->SetName("EngineLib"); + if (EngineLIBAssetPackage == nullptr) + { + LOG.Add("FEResourceManager::CreateEngineLIBAssetPackage: Error creating asset package", "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return nullptr; + } + + std::string EnginePath = FILE_SYSTEM.GetCurrentWorkingPath() + "/" + std::string(ENGINE_FOLDER) + "/"; + if (!FILE_SYSTEM.DoesDirectoryExist(EnginePath)) + { + LOG.Add("FEResourceManager::CreateEngineLIBAssetPackage: Engine folder does not exist", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return nullptr; + } + + std::vector AllFiles = FILE_SYSTEM.GetFilesInDirectory(EnginePath, true); + + std::vector DebugStrings; + // After having all files in the engine folder, we need to filter out only the lib files. + for (size_t i = 0; i < AllFiles.size(); i++) + { + if (AllFiles[i].substr(AllFiles[i].size() - 4) == ".lib") + { + // FIXME: Currently projects only need debug lib files. Is this correct? + // The code will grab either Debug or Release lib files. We should make this more deterministic. + // Note: Only FocalEngine.lib and FEBasicApplication.lib are needed. + if (AllFiles[i].find("FocalEngine.lib") == std::string::npos && AllFiles[i].find("FEBasicApplication.lib") == std::string::npos) + continue; + + FEAssetPackageEntryIntializeData EntryData; + EntryData.Name = FILE_SYSTEM.GetFileName(AllFiles[i]); + DebugStrings.push_back(EntryData.Name); + EntryData.Type = "BINARY"; + EntryData.Tag = ENGINE_RESOURCE_TAG; + EntryData.Comment = "Engine lib file"; + + EngineLIBAssetPackage->ImportAssetFromFile(AllFiles[i], EntryData); + } + } + + return EngineLIBAssetPackage; +} + +bool FEResourceManager::UnPackEngineLIBAssetPackage(FEAssetPackage* AssetPackage, std::string Path) +{ + if (AssetPackage == nullptr) + { + LOG.Add("FEResourceManager::UnPackEngineLIBAssetPackage: Asset package is nullptr", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + if (Path.empty()) + { + LOG.Add("FEResourceManager::UnPackEngineLIBAssetPackage: Destination path is empty", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + if (!FILE_SYSTEM.DoesDirectoryExist(Path)) + { + LOG.Add("FEResourceManager::UnPackEngineLIBAssetPackage: Destination path does not exist", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + std::vector AssetPackageContent = AssetPackage->GetEntryList(); + if (AssetPackageContent.empty()) + { + LOG.Add("FEResourceManager::UnPackEngineLIBAssetPackage: Asset package is empty", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + for (size_t i = 0; i < AssetPackageContent.size(); i++) + { + // Now we are ready to write the file. + if (!AssetPackage->ExportAssetToFile(AssetPackageContent[i].ID, Path + AssetPackageContent[i].Name)) + { + LOG.Add("FEResourceManager::UnPackEngineHeadersAssetPackage: Error exporting asset " + AssetPackageContent[i].ID + " to " + Path + AssetPackageContent[i].Name, "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + } + + return true; +} + +bool FEResourceManager::CopyEngineFiles(bool bCopyEngineHeaders, bool bCopyEngineSourceFiles, bool bCopyEngineLIBs, std::string DestinationDirectory) +{ + if (!FILE_SYSTEM.DoesDirectoryExist(DestinationDirectory)) + { + LOG.Add("FEResourceManager::CopyEngineFiles: DestinationDirectory does not exist", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + if (bCopyEngineHeaders) + { + FEAssetPackage* EngineHeadersPackage = RESOURCE_MANAGER.CreateEngineHeadersAssetPackage(); + if (EngineHeadersPackage == nullptr) + { + LOG.Add("FEResourceManager::CopyEngineFiles: Error creating engine headers asset package.", "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + + if (!RESOURCE_MANAGER.UnPackEngineHeadersAssetPackage(EngineHeadersPackage, DestinationDirectory + "SubSystems/FocalEngine/")) + { + LOG.Add("FEResourceManager::CopyEngineFiles: Error unpacking engine headers asset package.", "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + } + + if (bCopyEngineSourceFiles) + { + FEAssetPackage* EngineSourcePackage = RESOURCE_MANAGER.CreateEngineSourceFilesAssetPackage(); + if (EngineSourcePackage == nullptr) + { + LOG.Add("FEResourceManager::CopyEngineFiles: Error creating engine source files asset package.", "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + + if (!RESOURCE_MANAGER.UnPackEngineSourceFilesAssetPackage(EngineSourcePackage, DestinationDirectory + "SubSystems/FocalEngine/")) + { + LOG.Add("FEResourceManager::CopyEngineFiles: Error unpacking engine source files asset package.", "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + } + + if (bCopyEngineLIBs) + { + FEAssetPackage* EngineLIBPackage = RESOURCE_MANAGER.CreateEngineLIBAssetPackage(); + if (EngineLIBPackage == nullptr) + { + LOG.Add("FEResourceManager::CopyEngineFiles: Error creating engine lib asset package.", "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + + if (!RESOURCE_MANAGER.UnPackEngineLIBAssetPackage(EngineLIBPackage, DestinationDirectory)) + { + LOG.Add("FEResourceManager::CopyEngineFiles: Error unpacking engine lib asset package.", "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + } + + return true; +} + +FEAssetPackage* FEResourceManager::CreatePrivateEngineAssetPackage() +{ + FEAssetPackage* PrivateEngineAssetPackage = new FEAssetPackage(); + PrivateEngineAssetPackage->SetName("PrivateEngineResources"); + if (PrivateEngineAssetPackage == nullptr) + { + LOG.Add("FEResourceManager::CreatePrivateEngineAssetPackage: Error creating asset package", "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return nullptr; + } + + std::string EnginePath = FILE_SYSTEM.GetCurrentWorkingPath() + "/" + std::string(ENGINE_FOLDER) + "/"; + if (!FILE_SYSTEM.DoesDirectoryExist(EnginePath)) + { + LOG.Add("FEResourceManager::CreatePrivateEngineAssetPackage: Engine folder does not exist", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return nullptr; + } + + // TODO: Check if we need to add more files to the asset package. + // Currently dumping all related files from the engine folder into the asset package. + std::vector AllFiles = FILE_SYSTEM.GetFilesInDirectory(EnginePath + "/Resources/", false); + for (size_t i = 0; i < AllFiles.size(); i++) + { + if (AllFiles[i].substr(AllFiles[i].size() - 8) == ".texture" || + AllFiles[i].substr(AllFiles[i].size() - 6) == ".model" || + AllFiles[i].substr(AllFiles[i].size() - 19) == ".nativescriptmodule") + { + FEAssetPackageEntryIntializeData EntryData; + // Also since FEAssetPackage does not support folders, we need to save folder structure in the file name. + // But we will erase the engine folder path from the file name. + EntryData.Name = AllFiles[i].substr(FILE_SYSTEM.GetCurrentWorkingPath().size()); + EntryData.Type = AllFiles[i].substr(AllFiles[i].size() - 8) == ".texture" ? "FE_TEXTURE" : AllFiles[i].substr(AllFiles[i].size() - 19) == ".nativescriptmodule" ? "FE_NATIVE_SCRIPT_MODULE" : "FE_GAME_MODEL"; + EntryData.Tag = ENGINE_RESOURCE_TAG; + EntryData.Comment = ""; + + PrivateEngineAssetPackage->ImportAssetFromFile(AllFiles[i], EntryData); + } + } + + // And files from CoreExtensions. + AllFiles = FILE_SYSTEM.GetFilesInDirectory(EnginePath + "/CoreExtensions/", true); + for (size_t i = 0; i < AllFiles.size(); i++) + { + if (AllFiles[i].substr(AllFiles[i].size() - 5) == ".glsl") + { + FEAssetPackageEntryIntializeData EntryData; + // Also since FEAssetPackage does not support folders, we need to save folder structure in the file name. + // But we will erase the engine folder path from the file name. + EntryData.Name = AllFiles[i].substr(FILE_SYSTEM.GetCurrentWorkingPath().size()); + EntryData.Type = "GLSL"; + EntryData.Tag = ENGINE_RESOURCE_TAG; + EntryData.Comment = ""; + + PrivateEngineAssetPackage->ImportAssetFromFile(AllFiles[i], EntryData); + } + } + + return PrivateEngineAssetPackage; +} + +bool FEResourceManager::UnPackPrivateEngineAssetPackage(FEAssetPackage* AssetPackage, std::string Path) +{ + if (AssetPackage == nullptr) + { + LOG.Add("FEResourceManager::UnPackPrivateEngineAssetPackage: Asset package is nullptr", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + if (Path.empty()) + { + LOG.Add("FEResourceManager::UnPackPrivateEngineAssetPackage: Destination path is empty", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + if (!FILE_SYSTEM.DoesDirectoryExist(Path)) + { + LOG.Add("FEResourceManager::UnPackPrivateEngineAssetPackage: Destination path does not exist", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + std::vector AssetPackageContent = AssetPackage->GetEntryList(); + if (AssetPackageContent.empty()) + { + LOG.Add("FEResourceManager::UnPackPrivateEngineAssetPackage: Asset package is empty", "FE_RESOURCE_MANAGER", FE_LOG_WARNING); + return false; + } + + for (size_t i = 0; i < AssetPackageContent.size(); i++) + { + std::string LocalPath = std::filesystem::path(AssetPackageContent[i].Name).parent_path().string(); + // Since we are not using folders in FEAssetPackage, we need to create all folders in the file path. + // First we need to get chain of folders. + std::vector FolderChain; + try + { + std::filesystem::path Directory(LocalPath); + while (!Directory.string().empty()) + { + if (!FolderChain.empty()) + { + if (FolderChain.back() == Directory.string()) + break; + } + FolderChain.push_back(Directory.string()); + Directory = Directory.parent_path(); + } + + std::reverse(FolderChain.begin(), FolderChain.end()); + } + catch (const std::exception& Exception) + { + LOG.Add("Error in FEResourceManager::UnPackPrivateEngineAssetPackage: " + std::string(Exception.what()), "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + + // Then we will go from the root folder to the last folder and create them if they do not exist. + for (size_t j = 0; j < FolderChain.size(); j++) + { + std::string FinalPath = Path + FolderChain[j]; + if (!FILE_SYSTEM.DoesDirectoryExist(FinalPath)) + { + if (!FILE_SYSTEM.CreateDirectory(FinalPath)) + { + LOG.Add("FEResourceManager::UnPackPrivateEngineAssetPackage: Error creating directory " + FinalPath, "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + } + } + + // Now we are ready to write the file. + if (!AssetPackage->ExportAssetToFile(AssetPackageContent[i].ID, Path + AssetPackageContent[i].Name)) + { + LOG.Add("FEResourceManager::UnPackPrivateEngineAssetPackage: Error exporting asset " + AssetPackageContent[i].ID + " to " + Path + AssetPackageContent[i].Name, "FE_RESOURCE_MANAGER", FE_LOG_ERROR); + return false; + } + } + + return true; } \ No newline at end of file diff --git a/ResourceManager/FEResourceManager.h b/ResourceManager/FEResourceManager.h index bab5169..b317799 100644 --- a/ResourceManager/FEResourceManager.h +++ b/ResourceManager/FEResourceManager.h @@ -1,23 +1,29 @@ #pragma once #include "../Renderer/FEPostProcess.h" -#include "../Renderer/FETerrain.h" #include "../ThirdParty/lodepng/lodepng.h" #include "../ThirdParty/stb_image/stb_image.h" #include "FEGLTFLoader.h" +#include "../FileSystem/FEAssetPackage.h" + +#include "../SubSystems/Scene/FEPrefab.h" +#include "../SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptModule.h" #include "Config.h" +#define ENGINE_RESOURCE_TAG "ENGINE_PRIVATE_RESOURCE" + namespace FocalEngine { - class FEngine; - class FEScene; - class FERenderer; - - class FEResourceManager + class FOCAL_ENGINE_API FEResourceManager { - friend FEngine; - friend FEScene; - friend FERenderer; + friend class FEngine; + friend class FEScene; + friend class FERenderer; + friend class FETerrainSystem; + friend class FESkyDomeSystem; + friend class FEVirtualUIContext; + friend class FEVirtualUISystem; + friend class FENativeScriptSystem; public: SINGLETON_PUBLIC_PART(FEResourceManager) @@ -25,11 +31,10 @@ namespace FocalEngine const char* TessControlText = nullptr, const char* TessEvalText = nullptr, const char* GeometryText = nullptr, const char* ComputeText = nullptr, std::string ForceObjectID = ""); - bool MakeShaderStandard(FEShader* Shader); FEShader* GetShader(std::string ShaderID); std::vector GetShaderByName(std::string Name); - std::vector GetShadersList(); - std::vector GetStandardShadersList(); + std::vector GetShaderIDList(); + std::vector GetEnginePrivateShaderIDList(); void DeleteShader(const FEShader* Shader); bool ReplaceShader(std::string OldShaderID, FEShader* NewShader); std::string LoadGLSL(const char* FileName); @@ -41,8 +46,6 @@ namespace FocalEngine FETexture* LoadFETextureUnmanaged(const char* FileName, std::string Name = ""); FETexture* LoadFETexture(char* FileData, std::string Name = "", FETexture* ExistingTexture = nullptr); FETexture* LoadFETextureAsync(const char* FileName, std::string Name = "", FETexture* ExistingTexture = nullptr, std::string ForceObjectID = ""); - FETexture* LoadPNGHeightmap(const char* FileName, FETerrain* Terrain, std::string Name = ""); - FETexture* LoadFEHeightmap(const char* FileName, FETerrain* Terrain, std::string Name = ""); FETexture* RawDataToFETexture(unsigned char* TextureData, int Width, int Height, GLint Internalformat = -1, GLenum Format = GL_RGBA, GLenum Type = GL_UNSIGNED_BYTE); std::vector ChannelsToFETextures(FETexture* SourceTexture); unsigned char* ResizeTextureRawData(FETexture* SourceTexture, size_t TargetWidth, size_t TargetHeight, int FiltrationLevel = 0); @@ -54,15 +57,18 @@ namespace FocalEngine bool ExportFETextureToPNG(FETexture* TextureToExport, const char* FileName); bool ExportRawDataToPNG(const char* FileName, const unsigned char* TextureData, int Width, int Height, GLint Internalformat); void DeleteFETexture(const FETexture* Texture); - std::vector GetTextureList(); + std::vector GetTextureIDList(); FETexture* GetTexture(std::string ID); std::vector GetTextureByName(std::string Name); - bool MakeTextureStandard(FETexture* Texture); FETexture* NoTexture; FETexture* CreateTexture(GLint InternalFormat, GLenum Format, int Width, int Height, bool bUnManaged = true, std::string Name = ""); - FETexture* CreateSameFormatTexture(FETexture* ExampleTexture, int DifferentW = 0, int DifferentH = 0, bool bUnManaged = true, std::string Name = ""); + FETexture* CreateSameFormatTexture(FETexture* ReferenceTexture, int DifferentW = 0, int DifferentH = 0, bool bUnManaged = true, std::string Name = ""); + FETexture* CreateCopyOfTexture(FETexture* ReferenceTexture, bool bUnManaged = true, std::string Name = ""); + FETexture* CreateBlankHightMapTexture(int Width, int Height, std::string Name = ""); void AddTextureToManaged(FETexture* Texture); + FETexture* ImportTexture(const char* FileName); + FEMesh* RawDataToMesh(std::vector& Positions, std::vector& Normals, std::vector& Tangents, std::vector& UV, std::vector& Index, std::string Name = ""); FEMesh* RawDataToMesh(float* Positions, int PosSize, float* UV, int UVSize, @@ -74,10 +80,10 @@ namespace FocalEngine std::string Name = ""); void DeleteFEMesh(const FEMesh* Mesh); - bool MakeMeshStandard(FEMesh* Mesh); + bool ExportFEMeshToOBJ(FEMesh* MeshToExport, const char* FileName); - std::vector GetMeshList(); - std::vector GetStandardMeshList(); + std::vector GetMeshIDList(); + std::vector GetEnginePrivateMeshIDList(); FEMesh* GetMesh(std::string ID); std::vector GetMeshByName(std::string Name); std::vector ImportOBJ(const char* FileName, bool bForceOneMesh = false); @@ -87,94 +93,125 @@ namespace FocalEngine FEFramebuffer* CreateFramebuffer(int Attachments, int Width, int Height, bool bHDR = true); - std::vector GetMaterialList(); - std::vector GetStandardMaterialList(); + std::vector GetMaterialIDList(); + std::vector GetEnginePrivateMaterialIDList(); FEMaterial* GetMaterial(std::string ID); std::vector GetMaterialByName(std::string Name); FEMaterial* CreateMaterial(std::string Name = "", std::string ForceObjectID = ""); - bool MakeMaterialStandard(FEMaterial* Material); + Json::Value SaveMaterialToJSON(FEMaterial* Material); + FEMaterial* LoadMaterialFromJSON(Json::Value& Root); void DeleteMaterial(const FEMaterial* Material); - std::vector GetGameModelList(); - std::vector GetStandardGameModelList(); + std::vector GetGameModelIDList(); + std::vector GetEnginePrivateGameModelIDList(); FEGameModel* GetGameModel(std::string ID); std::vector GetGameModelByName(std::string Name); FEGameModel* CreateGameModel(FEMesh* Mesh = nullptr, FEMaterial* Material = nullptr, std::string Name = "", std::string ForceObjectID = ""); - bool MakeGameModelStandard(FEGameModel* GameModel); + Json::Value SaveGameModelToJSON(FEGameModel* GameModel); + FEGameModel* LoadGameModelFromJSON(Json::Value& Root); void DeleteGameModel(const FEGameModel* GameModel); - std::vector GetPrefabList(); - std::vector GetStandardPrefabList(); + std::vector GetPrefabIDList(); + std::vector GetEnginePrivatePrefabIDList(); FEPrefab* GetPrefab(std::string ID); std::vector GetPrefabByName(std::string Name); - FEPrefab* CreatePrefab(FEGameModel* GameModel = nullptr, std::string Name = "", std::string ForceObjectID = ""); - bool MakePrefabStandard(FEPrefab* Prefab); + FEPrefab* CreatePrefab(std::string Name = "", std::string ForceObjectID = "", FEScene* SceneDescription = nullptr); + Json::Value SavePrefabToJSON(FEPrefab* Prefab); + FEPrefab* LoadPrefabFromJSON(Json::Value& Root); void DeletePrefab(const FEPrefab* Prefab); - FETerrain* CreateTerrain(bool bCreateHeightMap = true, std::string Name = "", std::string ForceObjectID = ""); - void ActivateTerrainVacantLayerSlot(FETerrain* Terrain, FEMaterial* Material); - void LoadTerrainLayerMask(const char* FileName, FETerrain* Terrain, size_t LayerIndex); - void SaveTerrainLayerMask(const char* FileName, const FETerrain* Terrain, size_t LayerIndex); - void FillTerrainLayerMask(const FETerrain* Terrain, size_t LayerIndex); - void ClearTerrainLayerMask(const FETerrain* Terrain, size_t LayerIndex); - void DeleteTerrainLayerMask(FETerrain* Terrain, size_t LayerIndex); + std::vector GetNativeScriptModuleIDList(); + std::vector GetEnginePrivateNativeScriptModuleIDList(); + FENativeScriptModule* GetNativeScriptModule(std::string ID); + std::string ReadDLLModuleID(std::string DLLFilePath); + std::vector GetNativeScriptModuleByName(std::string Name); + FENativeScriptModule* CreateNativeScriptModule(std::string Name = "", std::string ForceObjectID = ""); + FENativeScriptModule* CreateNativeScriptModule(std::string DebugDLLFilePath, std::string DebugPDBFilePath, std::string ReleaseDLLFilePath, std::vector ScriptFiles = {}, std::string Name = "", std::string ForceObjectID = ""); + FENativeScriptModule* LoadFENativeScriptModule(std::string FileName); + void SaveFENativeScriptModule(FENativeScriptModule* NativeScriptModule, std::string FileName); + + FEAssetPackage* CreateEngineHeadersAssetPackage(); + bool UnPackEngineHeadersAssetPackage(FEAssetPackage* AssetPackage, std::string Path); + FEAssetPackage* CreateEngineSourceFilesAssetPackage(); + bool UnPackEngineSourceFilesAssetPackage(FEAssetPackage* AssetPackage, std::string Path); + FEAssetPackage* CreateEngineLIBAssetPackage(); + bool UnPackEngineLIBAssetPackage(FEAssetPackage* AssetPackage, std::string Path); + bool CopyEngineFiles(bool bCopyEngineHeaders, bool bCopyEngineSourceFiles, bool bCopyEngineLIBs, std::string DestinationDirectory); + + FEAssetPackage* CreatePrivateEngineAssetPackage(); + bool UnPackPrivateEngineAssetPackage(FEAssetPackage* AssetPackage, std::string Path); void Clear(); void LoadStandardMeshes(); void LoadStandardMaterial(); void LoadStandardGameModels(); - void LoadStandardPrefabs(); - void ReSaveStandardTextures(); + void ReSaveEnginePrivateTextures(); void ReSaveStandardMeshes(); std::string GetDefaultResourcesFolder(); - std::vector LoadGLTF(const char* FileName); - std::vector ImportAsset(const char* FileName); + // Returns true if the tag was set, false if the tag was not set. + bool SetTag(FEObject* Object, std::string NewTag); + + Json::Value SaveFEObjectPart(FEObject* Object); + FEObjectLoadedData LoadFEObjectPart(Json::Value Root); + + std::vector GetTagsThatWillPreventDeletion(); + void AddTagThatWillPreventDeletion(std::string Tag); + void RemoveTagThatWillPreventDeletion(std::string Tag); private: SINGLETON_PRIVATE_PART(FEResourceManager) std::unordered_map Shaders; - std::unordered_map StandardShaders; - - FETexture* CreateTexture(std::string Name = "", std::string ForceObjectID = ""); - FEMesh* CreateMesh(GLuint VaoID, unsigned int VertexCount, int VertexBuffersTypes, FEAABB AABB, std::string Name = ""); - - FEPostProcess* CreatePostProcess(int ScreenWidth, int ScreenHeight, std::string Name); std::unordered_map Textures; - std::unordered_map StandardTextures; - std::unordered_map Materials; - std::unordered_map StandardMaterials; - std::unordered_map Meshes; - std::unordered_map StandardMeshes; - std::unordered_map GameModels; - std::unordered_map StandardGameModels; - std::unordered_map Prefabs; - std::unordered_map StandardPrefabs; + std::unordered_map NativeScriptModules; - std::string GetFileNameFromFilePath(std::string FilePath); - FEEntity* CreateEntity(FEGameModel* GameModel, std::string Name, std::string ForceObjectID = ""); - FEEntity* CreateEntity(FEPrefab* Prefab, std::string Name, std::string ForceObjectID = ""); + FETexture* CreateTexture(std::string Name = "", std::string ForceObjectID = ""); + FEMesh* CreateMesh(GLuint VaoID, unsigned int VertexCount, int VertexBuffersTypes, FEAABB AABB, std::string Name = ""); - void InitTerrainEditTools(FETerrain* Terrain); + FEPostProcess* CreatePostProcess(int ScreenWidth, int ScreenHeight, std::string Name); + + std::string GetFileNameFromFilePath(std::string FilePath); std::string FreeObjectName(FE_OBJECT_TYPE ObjectType); GLint MaxColorAttachments = 1; - void FillTerrainLayerMaskWithRawData(const unsigned char* RawData, const FETerrain* Terrain, size_t LayerIndex); void CreateMaterialsFromOBJData(std::vector& ResultArray); static void LoadTextureFileAsyncCallBack(void* OutputData); std::string EngineFolder = std::string(ENGINE_FOLDER) + "/"; std::string ResourcesFolder = EngineFolder + "/Resources/"; - }; - #define RESOURCE_MANAGER FEResourceManager::getInstance() + std::vector TagsThatWillPreventDeletion = { ENGINE_RESOURCE_TAG }; + + template + void ClearResource(std::unordered_map& ResourceMap); + + void SetTagIternal(FEObject* Object, std::string NewTag); + + template + std::vector GetResourceIDListByTag(const std::unordered_map& Resources, const std::string& Tag); + + bool DeleteNativeScriptModuleInternal(FENativeScriptModule* Module); + + // TODO: Find a better way to handle resource extraction. + // These variables are used to extract engine resources after application build. + bool bUsePackageForPrivateResources = false; + FEAssetPackage* PrivateEngineAssetPackage = nullptr; + }; +#include "FEResourceManager.inl" + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetResourceManager(); + #define RESOURCE_MANAGER (*static_cast(GetResourceManager())) +#else + #define RESOURCE_MANAGER FEResourceManager::GetInstance() +#endif } \ No newline at end of file diff --git a/ResourceManager/FEResourceManager.inl b/ResourceManager/FEResourceManager.inl new file mode 100644 index 0000000..8ded935 --- /dev/null +++ b/ResourceManager/FEResourceManager.inl @@ -0,0 +1,37 @@ +#pragma once + +template +void FEResourceManager::ClearResource(std::unordered_map& ResourceMap) +{ + auto Iterator = ResourceMap.begin(); + while (Iterator != ResourceMap.end()) + { + if (std::find(TagsThatWillPreventDeletion.begin(), TagsThatWillPreventDeletion.end(), + Iterator->second->GetTag()) == TagsThatWillPreventDeletion.end()) + { + delete Iterator->second; + Iterator = ResourceMap.erase(Iterator); + } + else + { + Iterator++; + } + } +} + +template +std::vector FEResourceManager::GetResourceIDListByTag(const std::unordered_map& Resources, const std::string& Tag) +{ + std::vector Result; + + auto Iterator = Resources.begin(); + while (Iterator != Resources.end()) + { + if (Iterator->second->GetTag() == Tag) + Result.push_back(Iterator->second->GetObjectID()); + + Iterator++; + } + + return Result; +} \ No newline at end of file diff --git a/ResourceManager/FEglTFLoader.cpp b/ResourceManager/FEglTFLoader.cpp index 016f536..7f9ba56 100644 --- a/ResourceManager/FEglTFLoader.cpp +++ b/ResourceManager/FEglTFLoader.cpp @@ -1,8 +1,6 @@ #include "FEGLTFLoader.h" using namespace FocalEngine; -FEGLTFLoader* FEGLTFLoader::Instance = nullptr; - FEGLTFLoader::FEGLTFLoader() { @@ -19,11 +17,15 @@ void FEGLTFLoader::Clear() BufferViews.clear(); Accessors.clear(); Meshes.clear(); - Nodes.clear(); Images.clear(); + TextureSamplers.clear(); Textures.clear(); Materials.clear(); + + Nodes.clear(); + Scenes.clear(); + Scene = -1; } void FEGLTFLoader::LoadPrimitive(Json::Value JsonPrimitive, GLTFPrimitive& NewPrimitive) @@ -100,29 +102,42 @@ void FEGLTFLoader::LoadPrimitive(Json::Value JsonPrimitive, GLTFPrimitive& NewPr if (JsonPrimitive.isMember("material")) NewPrimitive.Material = JsonPrimitive["material"].asInt(); + if (JsonPrimitive.isMember("mode")) + { + NewPrimitive.Mode = JsonPrimitive["mode"].asInt(); + if (NewPrimitive.Mode < 0 || NewPrimitive.Mode > 6) + { + LOG.Add("Mode is not in range 0-6 in function FEGLTFLoader::LoadPrimitive.", "FE_LOG_LOADING", FE_LOG_ERROR); + } + + if (NewPrimitive.Mode != 4) + { + LOG.Add("Mesh.Primitive.Mode is not TRIANGLES, and is not supported, function FEGLTFLoader::LoadPrimitive.", "FE_LOG_LOADING", FE_LOG_ERROR); + } + } + LoadMeshRawData(NewPrimitive); } -GLTFMaterialTexture FEGLTFLoader::LoadMaterialTexture(Json::Value JsonTextureNode) +GLTFTextureInfo FEGLTFLoader::LoadTextureInfo(Json::Value JsonTextureNode) { - GLTFMaterialTexture TempTexture; + GLTFTextureInfo TempTextureInfo; if (JsonTextureNode.isMember("index")) { - TempTexture.Index = JsonTextureNode["index"].asInt(); + TempTextureInfo.Index = JsonTextureNode["index"].asInt(); } - - if (JsonTextureNode.isMember("texCoord")) + else { - TempTexture.TexCoord = JsonTextureNode["texCoord"].asInt(); + LOG.Add("Index is not present in function FEGLTFLoader::LoadTextureInfo.", "FE_LOG_LOADING", FE_LOG_ERROR); } - if (JsonTextureNode.isMember("scale")) + if (JsonTextureNode.isMember("texCoord")) { - TempTexture.TexCoord = JsonTextureNode["scale"].asInt(); + TempTextureInfo.TexCoord = JsonTextureNode["texCoord"].asInt(); } - return TempTexture; + return TempTextureInfo; } void FEGLTFLoader::Load(const char* FileName) @@ -159,7 +174,7 @@ void FEGLTFLoader::Load(const char* FileName) std::streamsize FileSize = File.tellg(); if (FileSize < 0) { - LOG.Add(std::string("can't load buffer from: ") + CurrentBuffer.Uri + " in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add(std::string("Can't load buffer from: ") + CurrentBuffer.Uri + " in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); } else { @@ -169,11 +184,19 @@ void FEGLTFLoader::Load(const char* FileName) } File.close(); - if (JsonBuffers[static_cast(i)].isMember("byteLength")) - CurrentBuffer.ByteLength = JsonBuffers[static_cast(i)]["byteLength"].asInt(); + // ByteLength is required. + if (!JsonBuffers[static_cast(i)].isMember("byteLength")) + { + LOG.Add("ByteLength is not present in buffer with index: " + std::to_string(i) + " in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + CurrentBuffer.ByteLength = JsonBuffers[static_cast(i)]["byteLength"].asInt(); if (CurrentBuffer.ByteLength != FileSize) - LOG.Add("byteLength and fileSize is not equal in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("ByteLength and fileSize is not equal in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + + if (JsonBuffers[static_cast(i)].isMember("name")) + CurrentBuffer.Name = JsonBuffers[static_cast(i)]["name"].asCString(); Buffers.push_back(CurrentBuffer); } @@ -186,18 +209,34 @@ void FEGLTFLoader::Load(const char* FileName) { GLTFBufferView CurrentBufferView; - if (JsonBufferViews[static_cast(i)].isMember("buffer")) - CurrentBufferView.Buffer = JsonBufferViews[static_cast(i)]["buffer"].asInt(); - - if (JsonBufferViews[static_cast(i)].isMember("byteLength")) - CurrentBufferView.ByteLength = JsonBufferViews[static_cast(i)]["byteLength"].asInt(); + // Buffer is required. + if (!JsonBufferViews[static_cast(i)].isMember("buffer")) + { + LOG.Add("Buffer is not present in bufferView with index: " + std::to_string(i) + " in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + CurrentBufferView.Buffer = JsonBufferViews[static_cast(i)]["buffer"].asInt(); if (JsonBufferViews[static_cast(i)].isMember("byteOffset")) CurrentBufferView.ByteOffset = JsonBufferViews[static_cast(i)]["byteOffset"].asInt(); + // ByteLength is required. + if (!JsonBufferViews[static_cast(i)].isMember("byteLength")) + { + LOG.Add("ByteLength is not present in bufferView with index: " + std::to_string(i) + " in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + CurrentBufferView.ByteLength = JsonBufferViews[static_cast(i)]["byteLength"].asInt(); + + if (JsonBufferViews[static_cast(i)].isMember("byteStride")) + CurrentBufferView.ByteStride = JsonBufferViews[static_cast(i)]["byteStride"].asInt(); + if (JsonBufferViews[static_cast(i)].isMember("target")) CurrentBufferView.Target = JsonBufferViews[static_cast(i)]["target"].asInt(); + if (JsonBufferViews[static_cast(i)].isMember("name")) + CurrentBufferView.Name = JsonBufferViews[static_cast(i)]["name"].asCString(); + BufferViews.push_back(CurrentBufferView); } } @@ -215,14 +254,57 @@ void FEGLTFLoader::Load(const char* FileName) if (JsonAccessors[static_cast(i)].isMember("byteOffset")) CurrentAccessor.ByteOffset = JsonAccessors[static_cast(i)]["byteOffset"].asInt(); - if (JsonAccessors[static_cast(i)].isMember("componentType")) - CurrentAccessor.ComponentType = JsonAccessors[static_cast(i)]["componentType"].asInt(); + // ComponentType is required. + if (!JsonAccessors[static_cast(i)].isMember("componentType")) + { + LOG.Add("ComponentType is not present in accessor with index: " + std::to_string(i) + " in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + CurrentAccessor.ComponentType = JsonAccessors[static_cast(i)]["componentType"].asInt(); + + if (JsonAccessors[static_cast(i)].isMember("normalized")) + CurrentAccessor.bNormalized = JsonAccessors[static_cast(i)]["normalized"].asBool(); + + // Count is required. + if (!JsonAccessors[static_cast(i)].isMember("count")) + { + LOG.Add("Count is not present in accessor with index: " + std::to_string(i) + " in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + CurrentAccessor.Count = JsonAccessors[static_cast(i)]["count"].asInt(); + + // Type is required. + if (!JsonAccessors[static_cast(i)].isMember("type")) + { + LOG.Add("Type is not present in accessor with index: " + std::to_string(i) + " in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + } + CurrentAccessor.Type = JsonAccessors[static_cast(i)]["type"].asString(); + + if (JsonAccessors[static_cast(i)].isMember("max")) + { + /*Json::Value JsonMax = JsonAccessors[static_cast(i)]["max"]; + for (size_t j = 0; j < JsonMax.size(); j++) + { + CurrentAccessor.Max.push_back(JsonMax[static_cast(j)].asInt()); + }*/ + } + + if (JsonAccessors[static_cast(i)].isMember("min")) + { + /*Json::Value JsonMin = JsonAccessors[static_cast(i)]["min"]; + for (size_t j = 0; j < JsonMin.size(); j++) + { + CurrentAccessor.Min.push_back(JsonMin[static_cast(j)].asInt()); + }*/ + } + + + // read GLTFAccessorSparse + + if (JsonAccessors[static_cast(i)].isMember("name")) + CurrentAccessor.Name = JsonAccessors[static_cast(i)]["name"].asCString(); - if (JsonAccessors[static_cast(i)].isMember("count")) - CurrentAccessor.Count = JsonAccessors[static_cast(i)]["count"].asInt(); - if (JsonAccessors[static_cast(i)].isMember("type")) - CurrentAccessor.Type = JsonAccessors[static_cast(i)]["type"].asString(); Accessors.push_back(CurrentAccessor); } @@ -257,11 +339,21 @@ void FEGLTFLoader::Load(const char* FileName) Json::Value JsonImages = Root["images"]; for (size_t i = 0; i < JsonImages.size(); i++) { + GLTFImage NewImage; + if (JsonImages[static_cast(i)].isMember("uri")) - { - std::string ImageUri = (Directory + JsonImages[static_cast(i)]["uri"].asCString()); - Images.push_back(ImageUri); - } + NewImage.Uri = JsonImages[static_cast(i)]["uri"].asCString(); + + if (JsonImages[static_cast(i)].isMember("mimeType")) + NewImage.MimeType = JsonImages[static_cast(i)]["mimeType"].asCString(); + + if (JsonImages[static_cast(i)].isMember("bufferView")) + NewImage.BufferView = JsonImages[static_cast(i)]["bufferView"].asInt(); + + if (JsonImages[static_cast(i)].isMember("name")) + NewImage.Name = JsonImages[static_cast(i)]["name"].asCString(); + + Images.push_back(NewImage); } } @@ -270,15 +362,44 @@ void FEGLTFLoader::Load(const char* FileName) Json::Value JsonTextures = Root["textures"]; for (size_t i = 0; i < JsonTextures.size(); i++) { + GLTFTexture NewTexture; + + if (JsonTextures[static_cast(i)].isMember("sampler")) + NewTexture.Sampler = JsonTextures[static_cast(i)]["sampler"].asInt(); + if (JsonTextures[static_cast(i)].isMember("source")) - { - int ImageIndex = JsonTextures[static_cast(i)]["source"].asInt(); - if (ImageIndex < Images.size()) - { - std::string TextureFile = Images[ImageIndex]; - Textures.push_back(TextureFile); - } - } + NewTexture.Source = JsonTextures[static_cast(i)]["source"].asInt(); + + if (JsonTextures[static_cast(i)].isMember("name")) + NewTexture.Name = JsonTextures[static_cast(i)]["name"].asCString(); + + Textures.push_back(NewTexture); + } + } + + if (Root.isMember("samplers")) + { + Json::Value JsonSamplers = Root["samplers"]; + for (size_t i = 0; i < JsonSamplers.size(); i++) + { + GLTFTextureSampler NewSampler; + + if (JsonSamplers[static_cast(i)].isMember("magFilter")) + NewSampler.MagFilter = JsonSamplers[static_cast(i)]["magFilter"].asInt(); + + if (JsonSamplers[static_cast(i)].isMember("minFilter")) + NewSampler.MinFilter = JsonSamplers[static_cast(i)]["minFilter"].asInt(); + + if (JsonSamplers[static_cast(i)].isMember("wrapS")) + NewSampler.WrapS = JsonSamplers[static_cast(i)]["wrapS"].asInt(); + + if (JsonSamplers[static_cast(i)].isMember("wrapT")) + NewSampler.WrapT = JsonSamplers[static_cast(i)]["wrapT"].asInt(); + + if (JsonSamplers[static_cast(i)].isMember("name")) + NewSampler.Name = JsonSamplers[static_cast(i)]["name"].asCString(); + + TextureSamplers.push_back(NewSampler); } } @@ -288,65 +409,89 @@ void FEGLTFLoader::Load(const char* FileName) for (size_t i = 0; i < JsonMaterials.size(); i++) { GLTFMaterial NewMaterial; - NewMaterial.Name = JsonMaterials[static_cast(i)]["name"].asCString(); + + if (JsonMaterials[static_cast(i)].isMember("name")) + NewMaterial.Name = JsonMaterials[static_cast(i)]["name"].asCString(); if (JsonMaterials[static_cast(i)].isMember("pbrMetallicRoughness")) { if (JsonMaterials[static_cast(i)]["pbrMetallicRoughness"].isMember("baseColorFactor")) { - NewMaterial.BaseColor = glm::vec4(JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["baseColorFactor"][0].asDouble(), - JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["baseColorFactor"][1].asDouble(), - JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["baseColorFactor"][2].asDouble(), - JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["baseColorFactor"][3].asDouble()); + NewMaterial.PBRMetallicRoughness.BaseColorFactor = glm::vec4(JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["baseColorFactor"][0].asDouble(), + JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["baseColorFactor"][1].asDouble(), + JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["baseColorFactor"][2].asDouble(), + JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["baseColorFactor"][3].asDouble()); } if (JsonMaterials[static_cast(i)]["pbrMetallicRoughness"].isMember("baseColorTexture")) - { - NewMaterial.BaseColorTexture = LoadMaterialTexture(JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["baseColorTexture"]); - /*if (jsonMaterials[int(i)]["pbrMetallicRoughness"]["baseColorTexture"].isMember("index")) - { - newMaterial.baseColorTexture.index = jsonMaterials[int(i)]["pbrMetallicRoughness"]["baseColorTexture"]["index"].asInt(); - } + NewMaterial.PBRMetallicRoughness.BaseColorTexture = LoadTextureInfo(JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["baseColorTexture"]); + - if (jsonMaterials[int(i)]["pbrMetallicRoughness"]["baseColorTexture"].isMember("texCoord")) - { - newMaterial.baseColorTexture.texCoord = jsonMaterials[int(i)]["pbrMetallicRoughness"]["baseColorTexture"]["texCoord"].asInt(); - } + if (JsonMaterials[static_cast(i)]["pbrMetallicRoughness"].isMember("metallicFactor")) + NewMaterial.PBRMetallicRoughness.MetallicFactor = static_cast(JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["metallicFactor"].asDouble()); - if (jsonMaterials[int(i)]["pbrMetallicRoughness"]["baseColorTexture"].isMember("scale")) - { - newMaterial.baseColorTexture.texCoord = jsonMaterials[int(i)]["pbrMetallicRoughness"]["baseColorTexture"]["scale"].asInt(); - }*/ - } + if (JsonMaterials[static_cast(i)]["pbrMetallicRoughness"].isMember("roughnessFactor")) + NewMaterial.PBRMetallicRoughness.RoughnessFactor = static_cast(JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["roughnessFactor"].asDouble()); if (JsonMaterials[static_cast(i)]["pbrMetallicRoughness"].isMember("metallicRoughnessTexture")) - { - NewMaterial.MetallicRoughnessTexture = LoadMaterialTexture(JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["metallicRoughnessTexture"]); - //if (jsonMaterials[int(i)]["pbrMetallicRoughness"]["metallicRoughnessTexture"].isMember("index")) - //{ - // newMaterial.metallicRoughnessTexture = jsonMaterials[int(i)]["pbrMetallicRoughness"]["metallicRoughnessTexture"]["index"].asInt(); - //} - } + NewMaterial.PBRMetallicRoughness.MetallicRoughnessTexture = LoadTextureInfo(JsonMaterials[static_cast(i)]["pbrMetallicRoughness"]["metallicRoughnessTexture"]); } if (JsonMaterials[static_cast(i)].isMember("normalTexture")) { - NewMaterial.NormalTexture = LoadMaterialTexture(JsonMaterials[static_cast(i)]["normalTexture"]); - /*if (jsonMaterials[int(i)]["normalTexture"].isMember("index")) + if (JsonMaterials[int(i)]["normalTexture"].isMember("index")) { - newMaterial.normalTexture = jsonMaterials[int(i)]["normalTexture"]["index"].asInt(); - }*/ + NewMaterial.NormalTexture.Index = JsonMaterials[int(i)]["normalTexture"]["index"].asInt(); + + if (JsonMaterials[int(i)]["normalTexture"].isMember("texCoord")) + NewMaterial.NormalTexture.TexCoord = JsonMaterials[int(i)]["normalTexture"]["texCoord"].asInt(); + + if (JsonMaterials[int(i)]["normalTexture"].isMember("scale")) + NewMaterial.NormalTexture.Scale = JsonMaterials[int(i)]["normalTexture"]["scale"].asFloat(); + } + else + { + LOG.Add("NormalTexture.Index is not present in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + } } if (JsonMaterials[static_cast(i)].isMember("occlusionTexture")) { - NewMaterial.OcclusionTexture = LoadMaterialTexture(JsonMaterials[static_cast(i)]["occlusionTexture"]); - /*if (jsonMaterials[int(i)]["occlusionTexture"].isMember("index")) + if (JsonMaterials[int(i)]["occlusionTexture"].isMember("index")) { - newMaterial.occlusionTexture = jsonMaterials[int(i)]["occlusionTexture"]["index"].asInt(); - }*/ + NewMaterial.OcclusionTexture.Index = JsonMaterials[int(i)]["occlusionTexture"]["index"].asInt(); + + if (JsonMaterials[int(i)]["occlusionTexture"].isMember("texCoord")) + NewMaterial.OcclusionTexture.TexCoord = JsonMaterials[int(i)]["occlusionTexture"]["texCoord"].asInt(); + + if (JsonMaterials[int(i)]["occlusionTexture"].isMember("strength")) + NewMaterial.OcclusionTexture.Strength = JsonMaterials[int(i)]["occlusionTexture"]["strength"].asFloat(); + } + else + { + LOG.Add("OcclusionTexture.Index is not present in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + } } + if (JsonMaterials[static_cast(i)].isMember("emissiveTexture")) + NewMaterial.EmissiveTexture = LoadTextureInfo(JsonMaterials[static_cast(i)]["emissiveTexture"]); + + if (JsonMaterials[static_cast(i)].isMember("emissiveFactor")) + { + NewMaterial.EmissiveFactor = glm::vec3(JsonMaterials[static_cast(i)]["emissiveFactor"][0].asDouble(), + JsonMaterials[static_cast(i)]["emissiveFactor"][1].asDouble(), + JsonMaterials[static_cast(i)]["emissiveFactor"][2].asDouble()); + } + + if (JsonMaterials[static_cast(i)].isMember("alphaMode")) + NewMaterial.AlphaMode = JsonMaterials[static_cast(i)]["alphaMode"].asCString(); + + if (JsonMaterials[static_cast(i)].isMember("alphaCutoff")) + NewMaterial.AlphaCutoff = static_cast(JsonMaterials[static_cast(i)]["alphaCutoff"].asDouble()); + + if (JsonMaterials[static_cast(i)].isMember("doubleSided")) + NewMaterial.bDoubleSided = JsonMaterials[static_cast(i)]["doubleSided"].asBool(); + Materials.push_back(NewMaterial); } } @@ -365,10 +510,64 @@ void FEGLTFLoader::Load(const char* FileName) bAnyInfoWasRead = true; } + if (JsonNodes[static_cast(i)].isMember("camera")) + { + int CameraIndex = JsonNodes[static_cast(i)]["camera"].asInt(); + if (CameraIndex < 0) + { + LOG.Add("Camera is less than 0 in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + } + else + { + NewNode.Camera = CameraIndex; + bAnyInfoWasRead = true; + } + } + if (JsonNodes[static_cast(i)].isMember("mesh")) { - NewNode.Mesh = JsonNodes[static_cast(i)]["mesh"].asInt(); - bAnyInfoWasRead = true; + int MeshIndex = JsonNodes[static_cast(i)]["mesh"].asInt(); + if (MeshIndex < 0) + { + LOG.Add("Mesh is less than 0 in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + } + else + { + NewNode.Mesh = MeshIndex; + bAnyInfoWasRead = true; + } + } + + if (JsonNodes[static_cast(i)].isMember("skin")) + { + int SkinIndex = JsonNodes[static_cast(i)]["skin"].asInt(); + if (SkinIndex < 0) + { + LOG.Add("Skin is less than 0 in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + + } + else + { + NewNode.Skin = SkinIndex; + bAnyInfoWasRead = true; + } + } + + if (JsonNodes[static_cast(i)].isMember("matrix")) + { + if (JsonNodes[static_cast(i)]["matrix"].size() != 16) + { + LOG.Add("Matrix size is not 16 in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + } + else + { + NewNode.Matrix.resize(16); + for (int j = 0; j < 16; j++) + { + NewNode.Matrix[j] = JsonNodes[static_cast(i)]["matrix"][j].asFloat(); + } + bAnyInfoWasRead = true; + } } if (JsonNodes[static_cast(i)].isMember("translation")) @@ -402,10 +601,28 @@ void FEGLTFLoader::Load(const char* FileName) if (JsonNodes[static_cast(i)].isMember("children")) { - Json::Value JsonChildrens = Root["children"]; + std::unordered_map ChildrensPresent; + + Json::Value JsonChildrens = JsonNodes[static_cast(i)]["children"]; for (size_t j = 0; j < JsonChildrens.size(); j++) { + int NewChild = JsonChildrens[static_cast(j)].asInt(); + // Each element in the array MUST be greater than or equal to 0. + if (NewChild < 0) + { + LOG.Add("Child is less than 0 in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + continue; + } + + // Each element in the array MUST be unique. + if (ChildrensPresent.find(NewChild) != ChildrensPresent.end()) + { + LOG.Add("Child is not unique in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + continue; + } + ChildrensPresent[NewChild] = true; + NewNode.Children.push_back(NewChild); } } @@ -413,13 +630,57 @@ void FEGLTFLoader::Load(const char* FileName) Nodes.push_back(NewNode); } } + + if (Root.isMember("scenes")) + { + Json::Value JsonScenes = Root["scenes"]; + for (size_t i = 0; i < JsonScenes.size(); i++) + { + GLTFScene NewScene; + + if (JsonScenes[static_cast(i)].isMember("name")) + NewScene.Name = JsonScenes[static_cast(i)]["name"].asCString(); + + if (JsonScenes[static_cast(i)].isMember("nodes")) + { + Json::Value JsonNodeIndexes = JsonScenes[static_cast(i)]["nodes"]; + for (size_t j = 0; j < JsonNodeIndexes.size(); j++) + { + int NewNodeIndex = JsonNodeIndexes[static_cast(j)].asInt(); + // Each element in the array MUST be greater than or equal to 0. + if (NewNodeIndex < 0) + { + LOG.Add("Node is less than 0 in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + continue; + } + + NewScene.RootChildren.push_back(NewNodeIndex); + } + } + + Scenes.push_back(NewScene); + } + } + + if (Root.isMember("scene")) + { + int SceneIndex = Root["scene"].asInt(); + if (SceneIndex < 0) + { + LOG.Add("Scene is less than 0 in function FEGLTFLoader::Load.", "FE_LOG_LOADING", FE_LOG_ERROR); + } + else + { + Scene = Root["scene"].asInt(); + } + } } bool FEGLTFLoader::LoadPositions(GLTFPrimitive& Primitive) { if (Accessors.size() <= Primitive.Attributes["POSITION"]) { - LOG.Add("primitive.attributes[\"POSITION\"] is out of bounds of accessors.size() function FEGLTFLoader::loadPositions.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("Primitive.Attributes[\"POSITION\"] is out of bounds of accessors.size() function FEGLTFLoader::LoadPositions.", "FE_LOG_LOADING", FE_LOG_ERROR); return false; } @@ -430,17 +691,19 @@ bool FEGLTFLoader::LoadPositions(GLTFPrimitive& Primitive) if (CurrentAccessor.ComponentType != 5126) { - LOG.Add("componentType is not float in function FEGLTFLoader::loadPositions.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("ComponentType is not float in function FEGLTFLoader::LoadPositions.", "FE_LOG_LOADING", FE_LOG_ERROR); return false; } int ByteOffset = 0; - if (CurrentBufferView.ByteOffset > 0) - ByteOffset = CurrentBufferView.ByteOffset; + /*if (CurrentAccessor.ByteOffset > 0) + ByteOffset = CurrentAccessor.ByteOffset;*/ + ByteOffset = CurrentBufferView.ByteOffset + CurrentAccessor.ByteOffset; // It is vec3 so size should be currentAccessor.count * 3. Primitive.RawData.Positions.resize(CurrentAccessor.Count * 3); - memcpy_s(Primitive.RawData.Positions.data(), CurrentBufferView.ByteLength, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), CurrentBufferView.ByteLength); + size_t TotalSize = GetTotalMemorySize(CurrentAccessor.Count, CurrentAccessor.ComponentType, CurrentAccessor.Type); + memcpy_s(Primitive.RawData.Positions.data(), TotalSize, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), TotalSize); return true; } @@ -449,7 +712,7 @@ bool FEGLTFLoader::LoadNomals(GLTFPrimitive& Primitive) { if (Accessors.size() <= Primitive.Attributes["NORMAL"]) { - LOG.Add("primitive.attributes[\"NORMAL\"] is out of bounds of accessors.size() function FEGLTFLoader::loadNomals.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("Primitive.Attributes[\"NORMAL\"] is out of bounds of accessors.size() function FEGLTFLoader::LoadNomals.", "FE_LOG_LOADING", FE_LOG_ERROR); return false; } @@ -460,17 +723,19 @@ bool FEGLTFLoader::LoadNomals(GLTFPrimitive& Primitive) if (CurrentAccessor.ComponentType != 5126) { - LOG.Add("componentType is not float in function FEGLTFLoader::loadNomals.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("ComponentType is not float in function FEGLTFLoader::LoadNomals.", "FE_LOG_LOADING", FE_LOG_ERROR); return false; } int ByteOffset = 0; - if (CurrentBufferView.ByteOffset > 0) - ByteOffset = CurrentBufferView.ByteOffset; + //if (CurrentAccessor.ByteOffset > 0) + //ByteOffset = CurrentAccessor.ByteOffset; + ByteOffset = CurrentBufferView.ByteOffset + CurrentAccessor.ByteOffset; // It is vec3 so size should be currentAccessor.count * 3. Primitive.RawData.Normals.resize(CurrentAccessor.Count * 3); - memcpy_s(Primitive.RawData.Normals.data(), CurrentBufferView.ByteLength, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), CurrentBufferView.ByteLength); + size_t TotalSize = GetTotalMemorySize(CurrentAccessor.Count, CurrentAccessor.ComponentType, CurrentAccessor.Type); + memcpy_s(Primitive.RawData.Normals.data(), TotalSize, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), TotalSize); return true; } @@ -479,7 +744,7 @@ bool FEGLTFLoader::LoadTangents(GLTFPrimitive& Primitive) { if (Accessors.size() <= Primitive.Attributes["TANGENT"]) { - LOG.Add("primitive.attributes[\"TANGENT\"] is out of bounds of accessors.size() function FEGLTFLoader::loadTangents.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("Primitive.Attributes[\"TANGENT\"] is out of bounds of accessors.size() function FEGLTFLoader::LoadTangents.", "FE_LOG_LOADING", FE_LOG_ERROR); return false; } @@ -490,24 +755,27 @@ bool FEGLTFLoader::LoadTangents(GLTFPrimitive& Primitive) if (CurrentAccessor.ComponentType != 5126) { - LOG.Add("componentType is not float in function FEGLTFLoader::loadTangents.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("ComponentType is not float in function FEGLTFLoader::LoadTangents.", "FE_LOG_LOADING", FE_LOG_ERROR); return false; } int ByteOffset = 0; - if (CurrentBufferView.ByteOffset > 0) - ByteOffset = CurrentBufferView.ByteOffset; + /*if (CurrentAccessor.ByteOffset > 0) + ByteOffset = CurrentAccessor.ByteOffset;*/ + ByteOffset = CurrentBufferView.ByteOffset + CurrentAccessor.ByteOffset; if (CurrentAccessor.Type == "VEC3") { Primitive.RawData.Tangents.resize(CurrentAccessor.Count * 3); - memcpy_s(Primitive.RawData.Tangents.data(), CurrentBufferView.ByteLength, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), CurrentBufferView.ByteLength); + size_t TotalSize = GetTotalMemorySize(CurrentAccessor.Count, CurrentAccessor.ComponentType, CurrentAccessor.Type); + memcpy_s(Primitive.RawData.Tangents.data(), TotalSize, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), TotalSize); } else if (CurrentAccessor.Type == "VEC4") { std::vector TempBuffer; TempBuffer.resize(CurrentAccessor.Count * 4); - memcpy_s(TempBuffer.data(), CurrentBufferView.ByteLength, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), CurrentBufferView.ByteLength); + size_t TotalSize = GetTotalMemorySize(CurrentAccessor.Count, CurrentAccessor.ComponentType, CurrentAccessor.Type); + memcpy_s(TempBuffer.data(), TotalSize, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), TotalSize); int iteration = 0; for (size_t i = 0; i < TempBuffer.size(); i++) @@ -538,7 +806,7 @@ bool FEGLTFLoader::LoadUV(GLTFPrimitive& Primitive) { if (Accessors.size() <= Primitive.Attributes[AttributeName]) { - LOG.Add("primitive.attributes[" + AttributeName + "] is out of bounds of accessors.size() function FEGLTFLoader::loadUV.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("Primitive.Attributes[" + AttributeName + "] is out of bounds of accessors.size() function FEGLTFLoader::LoadUV.", "FE_LOG_LOADING", FE_LOG_ERROR); //return false; } @@ -549,18 +817,20 @@ bool FEGLTFLoader::LoadUV(GLTFPrimitive& Primitive) if (CurrentAccessor.ComponentType != 5126) { - LOG.Add("componentType is not float in function FEGLTFLoader::loadUV.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("ComponentType is not float in function FEGLTFLoader::LoadUV.", "FE_LOG_LOADING", FE_LOG_ERROR); //return false; } int ByteOffset = 0; - if (CurrentBufferView.ByteOffset > 0) - ByteOffset = CurrentBufferView.ByteOffset; + //if (CurrentAccessor.ByteOffset > 0) + // ByteOffset = CurrentAccessor.ByteOffset; + ByteOffset = CurrentBufferView.ByteOffset + CurrentAccessor.ByteOffset; Primitive.RawData.UVs.resize(Primitive.RawData.UVs.size() + 1); // It is vec2 so size should be currentAccessor.count * 2. Primitive.RawData.UVs[i].resize(CurrentAccessor.Count * 2); - memcpy_s(Primitive.RawData.UVs[i].data(), CurrentBufferView.ByteLength, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), CurrentBufferView.ByteLength); + size_t TotalSize = GetTotalMemorySize(CurrentAccessor.Count, CurrentAccessor.ComponentType, CurrentAccessor.Type); + memcpy_s(Primitive.RawData.UVs[i].data(), TotalSize, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), TotalSize); } } @@ -571,7 +841,7 @@ bool FEGLTFLoader::LoadIndices(GLTFPrimitive& Primitive) { if (Accessors.size() <= Primitive.Indices) { - LOG.Add("primitive.indices is out of bounds of accessors.size() function FEGLTFLoader::loadIndices.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("Primitive.Indices is out of bounds of accessors.size() function FEGLTFLoader::LoadIndices.", "FE_LOG_LOADING", FE_LOG_ERROR); return false; } @@ -580,23 +850,52 @@ bool FEGLTFLoader::LoadIndices(GLTFPrimitive& Primitive) const GLTFBufferView CurrentBufferView = BufferViews[CurrentAccessor.BufferView]; const GLTFBuffer CurrentBuffer = Buffers[CurrentBufferView.Buffer]; - if (CurrentAccessor.ComponentType != 5123) + if (CurrentAccessor.ComponentType != 5121 && CurrentAccessor.ComponentType != 5123 && CurrentAccessor.ComponentType != 5125) { - LOG.Add("componentType is not unsigned short in function FEGLTFLoader::loadIndices.", "FE_LOG_LOADING", FE_LOG_ERROR); + LOG.Add("ComponentType is not unsigned byte, unsigned short or unsigned int in function FEGLTFLoader::LoadIndices.", "FE_LOG_LOADING", FE_LOG_ERROR); return false; } int ByteOffset = 0; - if (CurrentBufferView.ByteOffset > 0) - ByteOffset = CurrentBufferView.ByteOffset; + //if (CurrentAccessor.ByteOffset > 0) + // ByteOffset = CurrentAccessor.ByteOffset; + ByteOffset = CurrentBufferView.ByteOffset + CurrentAccessor.ByteOffset; + + if (CurrentAccessor.ComponentType == 5121) + { + std::vector TempBuffer; + TempBuffer.resize(CurrentAccessor.Count); + size_t TotalSize = GetTotalMemorySize(CurrentAccessor.Count, CurrentAccessor.ComponentType, CurrentAccessor.Type); + memcpy_s(TempBuffer.data(), TotalSize, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), TotalSize); - std::vector TempBuffer; - TempBuffer.resize(CurrentAccessor.Count); - memcpy_s(TempBuffer.data(), CurrentBufferView.ByteLength, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), CurrentBufferView.ByteLength); + for (size_t i = 0; i < TempBuffer.size(); i++) + { + Primitive.RawData.Indices.push_back((int)TempBuffer[i]); + } + } + else if (CurrentAccessor.ComponentType == 5123) + { + std::vector TempBuffer; + TempBuffer.resize(CurrentAccessor.Count); + size_t TotalSize = GetTotalMemorySize(CurrentAccessor.Count, CurrentAccessor.ComponentType, CurrentAccessor.Type); + memcpy_s(TempBuffer.data(), TotalSize, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), TotalSize); - for (size_t i = 0; i < TempBuffer.size(); i++) + for (size_t i = 0; i < TempBuffer.size(); i++) + { + Primitive.RawData.Indices.push_back((int)TempBuffer[i]); + } + } + else if (CurrentAccessor.ComponentType == 5125) { - Primitive.RawData.Indices.push_back((int)TempBuffer[i]); + std::vector TempBuffer; + TempBuffer.resize(CurrentAccessor.Count); + size_t TotalSize = GetTotalMemorySize(CurrentAccessor.Count, CurrentAccessor.ComponentType, CurrentAccessor.Type); + memcpy_s(TempBuffer.data(), TotalSize, reinterpret_cast(CurrentBuffer.RawData + ByteOffset), TotalSize); + + for (size_t i = 0; i < TempBuffer.size(); i++) + { + Primitive.RawData.Indices.push_back((int)TempBuffer[i]); + } } return true; @@ -604,50 +903,66 @@ bool FEGLTFLoader::LoadIndices(GLTFPrimitive& Primitive) bool FEGLTFLoader::LoadMeshRawData(GLTFPrimitive& Primitive) { - if (Primitive.Attributes.find("POSITION") == Primitive.Attributes.end()) + if (Primitive.Attributes.find("POSITION") != Primitive.Attributes.end()) { - LOG.Add("primitive.attributes does not contain \"POSITION\" in function FEGLTFLoader::loadMeshRawData.", "FE_LOG_LOADING", FE_LOG_ERROR); - return false; + if (!LoadPositions(Primitive)) + return false; } - if (Primitive.Attributes.find("NORMAL") == Primitive.Attributes.end()) + if (Primitive.Attributes.find("NORMAL") != Primitive.Attributes.end()) { - LOG.Add("primitive.attributes does not contain \"NORMAL\" in function FEGLTFLoader::loadMeshRawData.", "FE_LOG_LOADING", FE_LOG_ERROR); - return false; + if (!LoadNomals(Primitive)) + return false; } - if (Primitive.Attributes.find("TANGENT") == Primitive.Attributes.end()) + if (Primitive.Attributes.find("TANGENT") != Primitive.Attributes.end()) { - LOG.Add("primitive.attributes does not contain \"TANGENT\" in function FEGLTFLoader::loadMeshRawData.", "FE_LOG_LOADING", FE_LOG_ERROR); - return false; + if (!LoadTangents(Primitive)) + return false; } - if (Primitive.Attributes.find("TEXCOORD_0") == Primitive.Attributes.end()) + if (Primitive.Attributes.find("TEXCOORD_0") != Primitive.Attributes.end()) { - LOG.Add("primitive.attributes does not contain \"TEXCOORD_0\" in function FEGLTFLoader::loadMeshRawData.", "FE_LOG_LOADING", FE_LOG_ERROR); - return false; + if (!LoadUV(Primitive)) + return false; } - if (Primitive.Indices == -1) + if (Primitive.Indices != -1) { - LOG.Add("primitive.indices is -1 in function FEGLTFLoader::loadMeshRawData.", "FE_LOG_LOADING", FE_LOG_ERROR); - return false; + if (!LoadIndices(Primitive)) + return false; } - if (!LoadPositions(Primitive)) - return false; - - if (!LoadNomals(Primitive)) - return false; - - if (!LoadTangents(Primitive)) - return false; + return true; +} - if (!LoadUV(Primitive)) - return false; +size_t FEGLTFLoader::GetComponentSize(const int ComponentType) +{ + switch (ComponentType) + { + case 5120: return sizeof(int8_t); // BYTE + case 5121: return sizeof(uint8_t); // UNSIGNED_BYTE + case 5122: return sizeof(int16_t); // SHORT + case 5123: return sizeof(uint16_t); // UNSIGNED_SHORT + case 5125: return sizeof(uint32_t); // UNSIGNED_INT + case 5126: return sizeof(float); // FLOAT + default: return 0; + } +} - if (!LoadIndices(Primitive)) - return false; +size_t FEGLTFLoader::GetComponentCount(const std::string& Type) +{ + if (Type == "SCALAR") return 1; + if (Type == "VEC2") return 2; + if (Type == "VEC3") return 3; + if (Type == "VEC4") return 4; + if (Type == "MAT2") return 4; + if (Type == "MAT3") return 9; + if (Type == "MAT4") return 16; + return 0; +} - return true; +size_t FEGLTFLoader::GetTotalMemorySize(const int Count, const int ComponentType, const std::string& Type) +{ + return Count * GetComponentSize(ComponentType) * GetComponentCount(Type); } \ No newline at end of file diff --git a/ResourceManager/FEglTFLoader.h b/ResourceManager/FEglTFLoader.h index ef03f31..d079b94 100644 --- a/ResourceManager/FEglTFLoader.h +++ b/ResourceManager/FEglTFLoader.h @@ -4,33 +4,57 @@ namespace FocalEngine { - class FEResourceManager; - struct GLTFBuffer { std::string Uri; int ByteLength = -1; + std::string Name; + char* RawData = nullptr; }; struct GLTFBufferView { int Buffer = -1; + int ByteOffset = 0; int ByteLength = -1; - int ByteOffset = -1; + int ByteStride = -1; int Target = -1; + std::string Name; + }; + + struct GLTFAccessorSparseIndices + { + int BufferView = -1; + int ByteOffset = 0; + int ComponentType = -1; + }; + + struct GLTFAccessorSparseValues + { + int BufferView = -1; + int ByteOffset = 0; + }; + + struct GLTFAccessorSparse + { + int Count = -1; + int Indices = -1; + int Values = -1; }; struct GLTFAccessor { int BufferView = -1; - int ByteOffset = -1; + int ByteOffset = 0; int ComponentType = -1; + bool bNormalized = false; + std::string Type; int Count = -1; std::vector Max; std::vector Min; - - std::string Type; + GLTFAccessorSparse Sparse; + std::string Name; }; struct GLTFPrimitiveRawData @@ -48,34 +72,91 @@ namespace FocalEngine std::unordered_map Attributes; int Indices = -1; int Material = -1; - int Mode = -1; + // Default: TRIANGLES + int Mode = 4; GLTFPrimitiveRawData RawData; }; struct GLTFMesh { + std::vector Primitives; + std::string Name; + }; + + struct GLTFImage + { + std::string Uri; + std::string MimeType; + int BufferView = -1; std::string Name; std::vector Primitives; }; - struct GLTFMaterialTexture + struct GLTFTextureSampler + { + int MagFilter = -1; + int MinFilter = -1; + // Default: REPEAT + int WrapS = 10497; + // Default: REPEAT + int WrapT = 10497; + std::string Name; + }; + + struct GLTFTexture + { + int Sampler = -1; + int Source = -1; + std::string Name; + }; + + struct GLTFTextureInfo { int Index = -1; - int TexCoord = -1; - int Scale = 1; + int TexCoord = 0; + }; + + struct GLTFMaterialNormalTextureInfo + { + int Index = -1; + int TexCoord = 0; + float Scale = 1.0f; + }; + + struct GLTFMaterialOcclusionTextureInfo + { + int Index = -1; + int TexCoord = 0; + float Strength = 1.0f; + }; + + struct GLTFMaterialPBRMetallicRoughness + { + glm::vec4 BaseColorFactor = glm::vec4(1); + GLTFTextureInfo BaseColorTexture; + + float MetallicFactor = 1; + float RoughnessFactor = 1; + GLTFTextureInfo MetallicRoughnessTexture; }; struct GLTFMaterial { std::string Name; - glm::vec4 BaseColor = glm::vec4(-1); - GLTFMaterialTexture BaseColorTexture; + GLTFMaterialPBRMetallicRoughness PBRMetallicRoughness; + + GLTFMaterialNormalTextureInfo NormalTexture; + GLTFMaterialOcclusionTextureInfo OcclusionTexture; + + GLTFTextureInfo EmissiveTexture; + glm::vec3 EmissiveFactor = glm::vec3(-1); - GLTFMaterialTexture MetallicRoughnessTexture; - GLTFMaterialTexture NormalTexture; - GLTFMaterialTexture OcclusionTexture; + std::string AlphaMode = "OPAQUE"; + float AlphaCutoff = 0.5f; + + bool bDoubleSided = false; }; struct GLTFGameModel @@ -90,21 +171,41 @@ namespace FocalEngine struct GLTFNodes { std::string Name; + std::vector Children; + int Mesh = -1; + glm::vec3 Translation = glm::vec3(0); glm::quat Rotation = glm::quat(); glm::vec3 Scale = glm::vec3(1); + + // That is alternative to Translation, Rotation and Scale + // A floating-point 4x4 transformation matrix stored in column-major order. + std::vector Matrix; + + // The index of the camera referenced by this node. + int Camera = -1; + // When the node contains skin, all mesh.primitives MUST contain JOINTS_0 and WEIGHTS_0 attributes. + int Skin = -1; + }; + + struct GLTFScene + { + std::string Name; + // All nodes listed in scene.nodes array MUST be root nodes, i.e., they MUST NOT be listed in a node.children array of any node. The same root node MAY appear in multiple scenes. + std::vector RootChildren; }; class FEGLTFLoader { - friend FEResourceManager; + friend class FEResourceManager; + friend class FEScene; public: SINGLETON_PUBLIC_PART(FEGLTFLoader) void Load(const char* FileName); void Clear(); - + private: SINGLETON_PRIVATE_PART(FEGLTFLoader) @@ -112,7 +213,19 @@ namespace FocalEngine std::vector BufferViews; std::vector Accessors; std::vector Meshes; + + std::vector Images; + std::vector TextureSamplers; + GLTFTextureInfo LoadTextureInfo(Json::Value JsonTextureNode); + std::vector Textures; + std::vector Materials; + std::vector Nodes; + // A glTF asset that does not contain any scenes SHOULD be treated as a library of individual entities such as materials or meshes. + std::vector Scenes; + + // Identifies which of the scenes in the array SHOULD be displayed at load time. + int Scene = -1; void LoadPrimitive(Json::Value JsonPrimitive, GLTFPrimitive& NewPrimitive); bool LoadMeshRawData(GLTFPrimitive& Primitive); @@ -122,10 +235,8 @@ namespace FocalEngine bool LoadUV(GLTFPrimitive& Primitive); bool LoadIndices(GLTFPrimitive& Primitive); - GLTFMaterialTexture LoadMaterialTexture(Json::Value JsonTextureNode); - - std::vector Images; - std::vector Textures; - std::vector Materials; + size_t GetComponentSize(const int ComponentType); + size_t GetComponentCount(const std::string& Type); + size_t GetTotalMemorySize(const int Count, const int ComponentType, const std::string& Type); }; } \ No newline at end of file diff --git a/ResourceManager/Timestamp.h.in b/ResourceManager/Timestamp.h.in new file mode 100644 index 0000000..4edd49b --- /dev/null +++ b/ResourceManager/Timestamp.h.in @@ -0,0 +1,3 @@ +#pragma once + +#define ENGINE_BUILD_TIMESTAMP @ENGINE_BUILD_TIMESTAMP@ \ No newline at end of file diff --git a/Resources/19622421516E5B317E1B5360.model b/Resources/19622421516E5B317E1B5360.model index 0943e2b..c1d440f 100644 Binary files a/Resources/19622421516E5B317E1B5360.model and b/Resources/19622421516E5B317E1B5360.model differ diff --git a/Resources/2B7956623302254F620A675F.nativescriptmodule b/Resources/2B7956623302254F620A675F.nativescriptmodule new file mode 100644 index 0000000..0b794c2 Binary files /dev/null and b/Resources/2B7956623302254F620A675F.nativescriptmodule differ diff --git a/Resources/3F2118296C1E4533506A472E.texture b/Resources/3F2118296C1E4533506A472E.texture index 31b9bee..c5ede1d 100644 Binary files a/Resources/3F2118296C1E4533506A472E.texture and b/Resources/3F2118296C1E4533506A472E.texture differ diff --git a/Resources/45191B6F172E3B531978692E.model b/Resources/45191B6F172E3B531978692E.model index 51dc006..c75c961 100644 Binary files a/Resources/45191B6F172E3B531978692E.model and b/Resources/45191B6F172E3B531978692E.model differ diff --git a/Resources/456A31026A1C3152181A6064.texture b/Resources/456A31026A1C3152181A6064.texture index 2f4a119..fc82fae 100644 Binary files a/Resources/456A31026A1C3152181A6064.texture and b/Resources/456A31026A1C3152181A6064.texture differ diff --git a/Resources/48271F005A73241F5D7E7134.texture b/Resources/48271F005A73241F5D7E7134.texture index 832077e..00ce78e 100644 Binary files a/Resources/48271F005A73241F5D7E7134.texture and b/Resources/48271F005A73241F5D7E7134.texture differ diff --git a/Resources/637C784B2E5E5C6548190E1B.model b/Resources/637C784B2E5E5C6548190E1B.model index 0418b6f..d366e22 100644 Binary files a/Resources/637C784B2E5E5C6548190E1B.model and b/Resources/637C784B2E5E5C6548190E1B.model differ diff --git a/Resources/7F251E3E0D08013E3579315F.model b/Resources/7F251E3E0D08013E3579315F.model index 908a347..e4168b0 100644 Binary files a/Resources/7F251E3E0D08013E3579315F.model and b/Resources/7F251E3E0D08013E3579315F.model differ diff --git a/Resources/7F6057403249580D73311B54.texture b/Resources/7F6057403249580D73311B54.texture index 97c943c..55c717f 100644 Binary files a/Resources/7F6057403249580D73311B54.texture and b/Resources/7F6057403249580D73311B54.texture differ diff --git a/Resources/UserScriptsData/ExtractedScriptModules/2B7956623302254F620A675F/FirstScript.cpp b/Resources/UserScriptsData/ExtractedScriptModules/2B7956623302254F620A675F/FirstScript.cpp new file mode 100644 index 0000000..3e8387c --- /dev/null +++ b/Resources/UserScriptsData/ExtractedScriptModules/2B7956623302254F620A675F/FirstScript.cpp @@ -0,0 +1,227 @@ +#include "FirstScript.h" + +void FreeCameraController::Awake() +{ + +} + +void FreeCameraController::SetCursorToCenter() +{ + if (APPLICATION.GetMainWindow()->IsInFocus()) + { + FECameraComponent& CameraComponent = ParentEntity->GetComponent(); + const FEViewport* Viewport = CameraComponent.GetViewport(); + if (Viewport != nullptr) + { + int MainWindowXPosition, MainWindowYPosition; + APPLICATION.GetMainWindow()->GetPosition(&MainWindowXPosition, &MainWindowYPosition); + int CenterX = MainWindowXPosition + Viewport->GetX() + (Viewport->GetWidth() / 2); + int CenterY = MainWindowYPosition + Viewport->GetY() + (Viewport->GetHeight() / 2); + + RenderTargetCenterX = CenterX; + RenderTargetCenterY = CenterY; + } + + LastMouseX = RenderTargetCenterX; + LastMouseY = RenderTargetCenterY; + + INPUT.SetMousePosition(LastMouseX, LastMouseY); + FEInputMouseState MouseState = INPUT.GetMouseState(); + + LastMouseX = static_cast(MouseState.X); + LastMouseY = static_cast(MouseState.Y); + } +} + +void FreeCameraController::UpdateViewMatrix() +{ + if (ParentEntity == nullptr || !ParentEntity->HasComponent()) + return; + + FETransformComponent& TransformComponent = ParentEntity->GetComponent(); + FECameraComponent& CameraComponent = ParentEntity->GetComponent(); + + CameraComponent.SetViewMatrix(glm::inverse(TransformComponent.GetWorldMatrix())); +} + +void FreeCameraController::OnUpdate(double DeltaTime) +{ + if (ParentEntity == nullptr || !ParentEntity->HasComponent()) + return; + + FETransformComponent& TransformComponent = ParentEntity->GetComponent(); + FECameraComponent& CameraComponent = ParentEntity->GetComponent(); + + if (!bLastFrameWasActive && CameraComponent.IsActive()) + SetCursorToCenter(); + + bLastFrameWasActive = CameraComponent.IsActive(); + + FEInputMouseState MouseState = INPUT.GetMouseState(); + + const int MouseX = static_cast(MouseState.X); + const int MouseY = static_cast(MouseState.Y); + + if (CameraComponent.IsActive() && DeltaTime > 0.0) + { + glm::vec4 Forward = { 0.0f, 0.0f, -(MovementSpeed * 2) * (DeltaTime / 1000), 0.0f }; + glm::vec4 Right = { (MovementSpeed * 2) * (DeltaTime / 1000), 0.0f, 0.0f, 0.0f }; + + Right = Right * CameraComponent.GetViewMatrix(); + Forward = Forward * CameraComponent.GetViewMatrix(); + + glm::normalize(Right); + glm::normalize(Forward); + + glm::vec3 CurrentPosition = TransformComponent.GetPosition(); + + if (INPUT.GetKeyInfo(FEInputKey::FE_KEY_A).State != FE_RELEASED) + { + CurrentPosition.x -= Right.x; + CurrentPosition.y -= Right.y; + CurrentPosition.z -= Right.z; + } + + if (INPUT.GetKeyInfo(FEInputKey::FE_KEY_W).State != FE_RELEASED) + { + CurrentPosition.x += Forward.x; + CurrentPosition.y += Forward.y; + CurrentPosition.z += Forward.z; + } + + if (INPUT.GetKeyInfo(FEInputKey::FE_KEY_D).State != FE_RELEASED) + { + CurrentPosition.x += Right.x; + CurrentPosition.y += Right.y; + CurrentPosition.z += Right.z; + } + + if (INPUT.GetKeyInfo(FEInputKey::FE_KEY_S).State != FE_RELEASED) + { + CurrentPosition.x -= Forward.x; + CurrentPosition.y -= Forward.y; + CurrentPosition.z -= Forward.z; + } + + TransformComponent.SetPosition(CurrentPosition); + + // Rotation part. + if (LastMouseX == 0) LastMouseX = MouseX; + if (LastMouseY == 0) LastMouseY = MouseY; + + if (bFirstFrameActive) + { + bFirstFrameActive = false; + CurrentMouseYAngle = TransformComponent.GetRotation().x; + CurrentMouseXAngle = TransformComponent.GetRotation().y; + + return; + } + + if (LastMouseX < MouseX || abs(LastMouseX - MouseX) > CorrectionToSensitivity) + { + CurrentMouseXAngle -= (MouseX - LastMouseX) * 0.15f * static_cast((DeltaTime / 5.0)); + SetCursorToCenter(); + } + + if (LastMouseY < MouseY || abs(LastMouseY - MouseY) > CorrectionToSensitivity) + { + CurrentMouseYAngle -= (MouseY - LastMouseY) * 0.15f * static_cast((DeltaTime / 5.0)); + SetCursorToCenter(); + } + + TransformComponent.SetRotation(glm::vec3(CurrentMouseYAngle, CurrentMouseXAngle, 0.0f)); + } + + UpdateViewMatrix(); +} + +void ModelViewCameraController::Awake() +{ + +} + +glm::dvec3 ModelViewCameraController::PolarToCartesian(double PolarAngle, double AzimutAngle, const double R) +{ + PolarAngle *= glm::pi() / 180.0; + AzimutAngle *= glm::pi() / 180.0; + + const double X = R * sin(PolarAngle) * cos(AzimutAngle); + const double Y = R * sin(PolarAngle) * sin(AzimutAngle); + const double Z = R * cos(PolarAngle); + + return glm::dvec3(X, Z, Y); +} + +void ModelViewCameraController::UpdateViewMatrix() +{ + if (ParentEntity == nullptr || !ParentEntity->HasComponent()) + return; + + FETransformComponent& TransformComponent = ParentEntity->GetComponent(); + FECameraComponent& CameraComponent = ParentEntity->GetComponent(); + + glm::mat4 NewViewMatrix = glm::mat4(1.0f); + + glm::dvec3 NewPosition = PolarToCartesian(CurrentPolarAngle, CurrentAzimutAngle, DistanceToModel); + NewViewMatrix = glm::lookAt(NewPosition, glm::dvec3(0.0f), glm::dvec3(0, 1, 0)); + + NewViewMatrix = glm::translate(NewViewMatrix, -TargetPosition); + NewPosition += TargetPosition; + + glm::dvec3 Position; + glm::dquat Rotation; + glm::dvec3 Scale; + GEOMETRY.DecomposeMatrixToTranslationRotationScale(NewViewMatrix, Position, Rotation, Scale); + + // We need to udpate TransformComponent position and rotation. + TransformComponent.SetPosition(NewPosition); + TransformComponent.SetQuaternion(Rotation); + TransformComponent.ForceSetWorldMatrix(TransformComponent.GetLocalMatrix()); + + CameraComponent.SetViewMatrix(NewViewMatrix); +} + +void ModelViewCameraController::OnUpdate(double DeltaTime) +{ + if (ParentEntity == nullptr || !ParentEntity->HasComponent()) + return; + + FETransformComponent& TransformComponent = ParentEntity->GetComponent(); + FECameraComponent& CameraComponent = ParentEntity->GetComponent(); + + FEInputMouseState MouseState = INPUT.GetMouseState(); + const int MouseX = static_cast(MouseState.X); + const int MouseY = static_cast(MouseState.Y); + + if (CameraComponent.IsActive() && DeltaTime > 0.0) + { + float NewDistanceToModelValue = DistanceToModel + static_cast(MouseState.ScrollYOffset) * 2.0f * MouseWheelSensitivity; + if (NewDistanceToModelValue < 0.0f) + NewDistanceToModelValue = 0.1f; + + DistanceToModel = NewDistanceToModelValue; + + if (LastMouseX != MouseX) + { + CurrentAzimutAngle += (MouseX - LastMouseX) * 0.1f; + } + + if (LastMouseY != MouseY) + { + double NewValue = CurrentPolarAngle - (MouseY - LastMouseY) * 0.1f; + if (NewValue < 0.01) + NewValue = 0.011; + + if (NewValue > 179.98) + NewValue = 179.98; + + CurrentPolarAngle = NewValue; + } + } + + LastMouseX = MouseX; + LastMouseY = MouseY; + + UpdateViewMatrix(); +} \ No newline at end of file diff --git a/Resources/UserScriptsData/ExtractedScriptModules/2B7956623302254F620A675F/FirstScript.h b/Resources/UserScriptsData/ExtractedScriptModules/2B7956623302254F620A675F/FirstScript.h new file mode 100644 index 0000000..d17741c --- /dev/null +++ b/Resources/UserScriptsData/ExtractedScriptModules/2B7956623302254F620A675F/FirstScript.h @@ -0,0 +1,61 @@ +#include "../../FENativeScriptConnector.h" +using namespace FocalEngine; + +// DO NOT CHANGE THIS LINE. +SET_MODULE_ID("2B7956623302254F620A675F"); + +class FreeCameraController : public FENativeScriptCore +{ + int CorrectionToSensitivity = 2; + + int LastMouseX = 0; + int LastMouseY = 0; + + int RenderTargetCenterX = 0; + int RenderTargetCenterY = 0; + + float CurrentMouseXAngle = 0.0f; + float CurrentMouseYAngle = 0.0f; + + bool bLastFrameWasActive = false; + bool bFirstFrameActive = true; + + void SetCursorToCenter(); + void UpdateViewMatrix(); +public: + void Awake() override; + void OnUpdate(double DeltaTime) override; + void OnDestroy() override { /* ... */ } + + float MovementSpeed = 10.0f; +}; + +REGISTER_SCRIPT(FreeCameraController) +RUN_IN_EDITOR_MODE(FreeCameraController) +REGISTER_SCRIPT_FIELD(FreeCameraController, float, MovementSpeed) + +class ModelViewCameraController : public FENativeScriptCore +{ + int LastMouseX = 0; + int LastMouseY = 0; + + double CurrentPolarAngle = 90.0; + double CurrentAzimutAngle = 90.0; + + glm::dvec3 PolarToCartesian(double PolarAngle, double AzimutAngle, const double R); + void UpdateViewMatrix(); +public: + void Awake() override; + void OnUpdate(double DeltaTime) override; + void OnDestroy() override { /* ... */ } + + float DistanceToModel = 10.0; + glm::vec3 TargetPosition = glm::vec3(0.0f); + float MouseWheelSensitivity = 1.0f; +}; + +REGISTER_SCRIPT(ModelViewCameraController) +RUN_IN_EDITOR_MODE(ModelViewCameraController) +REGISTER_SCRIPT_FIELD(ModelViewCameraController, float, DistanceToModel) +REGISTER_SCRIPT_FIELD(ModelViewCameraController, glm::vec3, TargetPosition) +REGISTER_SCRIPT_FIELD(ModelViewCameraController, float, MouseWheelSensitivity) \ No newline at end of file diff --git a/Resources/UserScriptsData/FENativeScriptConnector.cpp b/Resources/UserScriptsData/FENativeScriptConnector.cpp new file mode 100644 index 0000000..2605e34 --- /dev/null +++ b/Resources/UserScriptsData/FENativeScriptConnector.cpp @@ -0,0 +1,37 @@ +// DO NOT CHANGE THIS FILE. +#include "FENativeScriptConnector.h" + +#ifdef FOCAL_ENGINE_SHARED + FECoreScriptManager::FECoreScriptManager() {}; + FECoreScriptManager::~FECoreScriptManager() {}; + + extern "C" __declspec(dllexport) const char* GetModuleID() + { + return CORE_SCRIPT_MANAGER.CurrentModuleID.c_str(); + } + + extern "C" __declspec(dllexport) void* GetScriptRegistry() + { + return &CORE_SCRIPT_MANAGER.GetRegistry(); + } + + #include "ResourceManager/Timestamp.h" + extern "C" __declspec(dllexport) unsigned long long GetEngineHeadersBuildVersion() + { + std::string StringRepresentation = ENGINE_BUILD_TIMESTAMP; + unsigned long long BuildVersion = std::stoull(StringRepresentation.c_str()); + return BuildVersion; + } + + extern "C" __declspec(dllexport) bool IsCompiledInDebugMode() + { + #ifdef _DEBUG + return true; + #else + return false; + #endif + } +#else + FEStaticCoreScriptManager::FEStaticCoreScriptManager() {}; + FEStaticCoreScriptManager::~FEStaticCoreScriptManager() {}; +#endif \ No newline at end of file diff --git a/Resources/UserScriptsData/FENativeScriptConnector.h b/Resources/UserScriptsData/FENativeScriptConnector.h new file mode 100644 index 0000000..fca6529 --- /dev/null +++ b/Resources/UserScriptsData/FENativeScriptConnector.h @@ -0,0 +1,202 @@ +// DO NOT CHANGE THIS FILE. +#include "../../../FEngine.h" +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED + // Expose function to query script information + extern "C" __declspec(dllexport) const char* GetModuleID(); + extern "C" __declspec(dllexport) void* GetScriptRegistry(); + extern "C" __declspec(dllexport) unsigned long long GetEngineHeadersBuildVersion(); + + extern "C" __declspec(dllexport) bool IsCompiledInDebugMode(); + + class FECoreScriptManager + { + public: + SINGLETON_PUBLIC_PART(FECoreScriptManager) + + using ScriptCreator = std::function; + + static void RegisterScript(const std::string& Name, ScriptCreator ConstructorFunction) + { + GetRegistry()[Name].ConstructorFunction = ConstructorFunction; + GetRegistry()[Name].Name = Name; + } + + static std::unordered_map& GetRegistry() + { + static std::unordered_map Registry; + return Registry; + } + + std::string CurrentModuleID = ""; + + private: + SINGLETON_PRIVATE_PART(FECoreScriptManager) + }; + + #define CORE_SCRIPT_MANAGER FECoreScriptManager::GetInstance() + + + #define REGISTER_SCRIPT(ScriptClass) \ + extern "C" __declspec(dllexport) FENativeScriptCore* Create##ScriptClass() { \ + return new ScriptClass(); \ + } \ + namespace { \ + struct Register##ScriptClass { \ + Register##ScriptClass() { \ + CORE_SCRIPT_MANAGER.RegisterScript(#ScriptClass, Create##ScriptClass); \ + } \ + } register##ScriptClass; \ + } + + #define REGISTER_SCRIPT_FIELD(ScriptClass, FieldType, FieldName) \ + namespace { \ + struct Register##ScriptClass##FieldName { \ + Register##ScriptClass##FieldName() { \ + CORE_SCRIPT_MANAGER.GetRegistry()[#ScriptClass].VariablesRegistry[#FieldName].Name = #FieldName; \ + CORE_SCRIPT_MANAGER.GetRegistry()[#ScriptClass].VariablesRegistry[#FieldName].Type = #FieldType; \ + \ + CORE_SCRIPT_MANAGER.GetRegistry()[#ScriptClass].VariablesRegistry[#FieldName].Getter = [](FENativeScriptCore* Base) -> std::any { \ + auto* Script = static_cast(Base); \ + return Script->FieldName; \ + }; \ + CORE_SCRIPT_MANAGER.GetRegistry()[#ScriptClass].VariablesRegistry[#FieldName].Setter = [](FENativeScriptCore* Base, const std::any& Value) {\ + auto* Script = static_cast(Base); \ + Script->FieldName = std::any_cast(Value); \ + }; \ + } \ + } register##ScriptClass##FieldName; \ + } + + #define RUN_IN_EDITOR_MODE(ScriptClass) \ + namespace { \ + struct RegisterRunInEditor##ScriptClass { \ + RegisterRunInEditor##ScriptClass() { \ + if (CORE_SCRIPT_MANAGER.GetRegistry().find(#ScriptClass) != CORE_SCRIPT_MANAGER.GetRegistry().end()) \ + CORE_SCRIPT_MANAGER.GetRegistry()[#ScriptClass].bRunInEditor = true; \ + } \ + } registerRunInEditor##ScriptClass; \ + } + + #define SET_MODULE_ID(ModuleID) \ + namespace { \ + struct FocalEngineRegisterModuleID { \ + FocalEngineRegisterModuleID() { \ + CORE_SCRIPT_MANAGER.CurrentModuleID = ModuleID; \ + } \ + } focalEngineRegisterModuleID; \ + } +#else + class FEStaticCoreScriptManager + { + public: + SINGLETON_PUBLIC_PART(FEStaticCoreScriptManager) + + using ScriptCreator = std::function; + + void RegisterScript(const std::string& Name, ScriptCreator ConstructorFunction) + { + auto& CurrentRegistry = GetRegistryForCurrentModule(); + CurrentRegistry[Name].ConstructorFunction = ConstructorFunction; + CurrentRegistry[Name].Name = Name; + } + + static std::unordered_map& GetRegistry() + { + static std::unordered_map Registry; + return Registry; + } + + std::unordered_map& GetRegistryForCurrentModule() + { + if (CurrentModuleID.empty()) + throw std::runtime_error("FEStaticCoreScriptManager::GetRegistryForCurrentModule Module ID is not set. Use SET_MODULE_ID macro before registering scripts."); + + return Registry[CurrentModuleID]; + } + + bool HaveModuleWithID(std::string& ModuleID) + { + return Registry.find(ModuleID) != Registry.end(); + } + + std::unordered_map GetRegistryForModuleWithID(std::string& ModuleID) + { + if (!HaveModuleWithID(ModuleID)) + { + LOG.Add("FEStaticCoreScriptManager::GetRegistryForModuleWithID: Module with ID " + ModuleID + " does not exist.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::unordered_map(); + } + + return Registry[ModuleID]; + } + + void SetCurrentModuleID(const std::string& ModuleID) + { + CurrentModuleID = ModuleID; + } + + private: + SINGLETON_PRIVATE_PART(FEStaticCoreScriptManager) + + std::string CurrentModuleID = ""; + std::unordered_map> Registry; + }; + + #define STATIC_CORE_SCRIPT_MANAGER FEStaticCoreScriptManager::GetInstance() + + + #define REGISTER_SCRIPT(ScriptClass) \ + FENativeScriptCore* Create##ScriptClass() { \ + return new ScriptClass(); \ + } \ + namespace { \ + struct Register##ScriptClass { \ + Register##ScriptClass() { \ + STATIC_CORE_SCRIPT_MANAGER.RegisterScript(#ScriptClass, Create##ScriptClass); \ + } \ + } register##ScriptClass; \ + } + + #define REGISTER_SCRIPT_FIELD(ScriptClass, FieldType, FieldName) \ + namespace { \ + struct Register##ScriptClass##FieldName { \ + Register##ScriptClass##FieldName() { \ + auto& ScriptData = STATIC_CORE_SCRIPT_MANAGER.GetRegistryForCurrentModule()[#ScriptClass].VariablesRegistry[#FieldName]; \ + ScriptData.Name = #FieldName; \ + ScriptData.Type = #FieldType; \ + \ + ScriptData.Getter = [](FENativeScriptCore* Base) -> std::any { \ + auto* Script = static_cast(Base); \ + return Script->FieldName; \ + }; \ + \ + ScriptData.Setter = [](FENativeScriptCore* Base, const std::any& Value) { \ + auto* Script = static_cast(Base); \ + Script->FieldName = std::any_cast(Value); \ + }; \ + } \ + } register##ScriptClass##FieldName; \ + } + + #define RUN_IN_EDITOR_MODE(ScriptClass) \ + namespace { \ + struct RegisterRunInEditor##ScriptClass { \ + RegisterRunInEditor##ScriptClass() { \ + auto& CurrentRegistry = STATIC_CORE_SCRIPT_MANAGER.GetRegistryForCurrentModule(); \ + if (CurrentRegistry.find(#ScriptClass) != CurrentRegistry.end()) \ + CurrentRegistry[#ScriptClass].bRunInEditor = true; \ + } \ + } registerRunInEditor##ScriptClass; \ + } + + #define SET_MODULE_ID(ModuleID) \ + namespace { \ + struct FocalEngineRegisterModuleID { \ + FocalEngineRegisterModuleID() { \ + STATIC_CORE_SCRIPT_MANAGER.SetCurrentModuleID(ModuleID); \ + } \ + } focalEngineRegisterModuleID; \ + } +#endif \ No newline at end of file diff --git a/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/DebugBuildActions.cmake b/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/DebugBuildActions.cmake new file mode 100644 index 0000000..4fe4bea --- /dev/null +++ b/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/DebugBuildActions.cmake @@ -0,0 +1,22 @@ +if(EXISTS "${CMAKE_BINARY_DIR}/BuildManagement/Build_Recursion_Guard.txt") + file(REMOVE "${CMAKE_BINARY_DIR}/BuildManagement/Build_Recursion_Guard.txt") +else() + if(EXISTS "${CMAKE_BINARY_DIR}/BuildManagement/Force_Build_Finished.txt") + file(REMOVE "${CMAKE_BINARY_DIR}/BuildManagement/Force_Build_Finished.txt") + endif() + + file(TOUCH "${CMAKE_BINARY_DIR}/BuildManagement/Build_Recursion_Guard.txt") + + execute_process( + COMMAND ${CMAKE_COMMAND} --build "${CMAKE_BINARY_DIR}" --target PLACE_HOLDER --config Release + RESULT_VARIABLE BuildResult + OUTPUT_VARIABLE BuildOutput + ) + + if(BuildResult) + message("Release Build failed with error ${BuildResult}: ${BuildOutput}") + else() + message("Release Build succeeded: ${BuildOutput}") + file(TOUCH "${CMAKE_BINARY_DIR}/BuildManagement/Force_Build_Finished.txt") + endif() +endif() \ No newline at end of file diff --git a/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/EnsureBuildCompletion.cmake b/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/EnsureBuildCompletion.cmake new file mode 100644 index 0000000..75e1d6e --- /dev/null +++ b/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/EnsureBuildCompletion.cmake @@ -0,0 +1,9 @@ +if(EXISTS "${CMAKE_BINARY_DIR}/BuildManagement/Debug_Build_Flag.txt") + file(REMOVE "${CMAKE_BINARY_DIR}/BuildManagement/Debug_Build_Flag.txt") + message("Debug build was conducted, forcing release build.") + include("${CMAKE_CURRENT_LIST_DIR}/DebugBuildActions.cmake") +elseif(EXISTS "${CMAKE_BINARY_DIR}/BuildManagement/Release_Build_Flag.txt") + file(REMOVE "${CMAKE_BINARY_DIR}/BuildManagement/Release_Build_Flag.txt") + message("Release build was conducted, forcing debug build.") + include("${CMAKE_CURRENT_LIST_DIR}/ReleaseBuildActions.cmake") +endif() \ No newline at end of file diff --git a/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/ReleaseBuildActions.cmake b/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/ReleaseBuildActions.cmake new file mode 100644 index 0000000..5335fd6 --- /dev/null +++ b/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/ReleaseBuildActions.cmake @@ -0,0 +1,22 @@ +if(EXISTS "${CMAKE_BINARY_DIR}/BuildManagement/Build_Recursion_Guard.txt") + file(REMOVE "${CMAKE_BINARY_DIR}/BuildManagement/Build_Recursion_Guard.txt") +else() + if(EXISTS "${CMAKE_BINARY_DIR}/BuildManagement/Force_Build_Finished.txt") + file(REMOVE "${CMAKE_BINARY_DIR}/BuildManagement/Force_Build_Finished.txt") + endif() + + file(TOUCH "${CMAKE_BINARY_DIR}/BuildManagement/Build_Recursion_Guard.txt") + + execute_process( + COMMAND ${CMAKE_COMMAND} --build "${CMAKE_BINARY_DIR}" --target PLACE_HOLDER --config Debug + RESULT_VARIABLE BuildResult + OUTPUT_VARIABLE BuildOutput + ) + + if(BuildResult) + message("Debug Build failed with error ${BuildResult}: ${BuildOutput}") + else() + message("Debug Build succeeded: ${BuildOutput}") + file(TOUCH "${CMAKE_BINARY_DIR}/BuildManagement/Force_Build_Finished.txt") + endif() +endif() \ No newline at end of file diff --git a/Resources/UserScriptsData/NativeScriptProjectData/CMakeLists_Template.txt b/Resources/UserScriptsData/NativeScriptProjectData/CMakeLists_Template.txt new file mode 100644 index 0000000..f2627c5 --- /dev/null +++ b/Resources/UserScriptsData/NativeScriptProjectData/CMakeLists_Template.txt @@ -0,0 +1,85 @@ +cmake_minimum_required(VERSION 3.10) + +set(BUILD_TYPE "Debug and Release" CACHE STRING "Choose Build type") +set(CMAKE_BUILD_TYPE Debug) +set(CMAKE_CONFIGURATION_TYPES Debug Release) + +add_definitions(-DUNICODE) +add_definitions(-D_UNICODE) + +set(PROJECT_NAME PLACE_HOLDER) + +project(${PROJECT_NAME}) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Set compiler flags based on USE_STATIC_RUNTIME +if(MSVC) + # Disable C4251 warning + add_compile_options(/wd4251) + + if(USE_STATIC_RUNTIME) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") + else() + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") + endif() + + # Always add /MP for multi-processor compilation + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + # Define that engine should be used as shared library + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D FEBASICAPPLICATION_SHARED /D FOCAL_ENGINE_SHARED") +endif() + +# Turn on the ability to create folders to organize projects (.vcproj) +# It creates "CMakePredefinedTargets" folder by default and adds CMake +# defined projects like INSTALL.vcproj and ZERO_CHECK.vcproj +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +file(GLOB Connection_To_Engine_SRC + "SubSystems/FocalEngine/Resources/UserScriptsData/FENativeScriptConnector.cpp" + "SubSystems/FocalEngine/Resources/UserScriptsData/FENativeScriptConnector.h" +) + +file(GLOB Main_SRC PLACE_HOLDER) + +link_directories(${GLEW_LIB_DIR}) +link_directories(${GLFW_LIB_DIR}) + +add_library(${PROJECT_NAME} + SHARED + ${Main_SRC} + ${Connection_To_Engine_SRC} +) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + FEBasicApplication.lib + FocalEngine.lib +) + +# FocalEngine is headers-only, we need to add its include directories +target_include_directories(${PROJECT_NAME} PRIVATE ${FOCAL_ENGINE_INCLUDES}) +target_compile_definitions(${PROJECT_NAME} PRIVATE FOCAL_ENGINE_HEADERS_ONLY) + +source_group("Source Files/" FILES ${Main_SRC}) +source_group("Source Files/Engine/" FILES ${Connection_To_Engine_SRC}) + +# set the startup project +set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME}) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/SubSystems/FocalEngine/SubSystems/FEBasicApplication + ${CMAKE_CURRENT_SOURCE_DIR}/SubSystems/FocalEngine/SubSystems/FEBasicApplication/SubSystems + ${CMAKE_CURRENT_SOURCE_DIR}/SubSystems/FocalEngine/SubSystems/FEBasicApplication/ThirdParty + ${CMAKE_CURRENT_SOURCE_DIR}/SubSystems/FocalEngine/SubSystems/FEBasicApplication/ThirdParty/glew2/include + ${CMAKE_CURRENT_SOURCE_DIR}/SubSystems/FocalEngine + ${CMAKE_CURRENT_SOURCE_DIR}/SubSystems/FocalEngine/ThirdParty + ${CMAKE_CURRENT_SOURCE_DIR}/SubSystems/FocalEngine/ThirdParty/entt +) + +add_custom_command( TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "Build type: $" > $,${CMAKE_CURRENT_BINARY_DIR}/BuildManagement/Debug_Build_Flag.txt,${CMAKE_CURRENT_BINARY_DIR}/BuildManagement/Release_Build_Flag.txt> + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_LIST_DIR}/BuildManagement/EnsureBuildCompletion.cmake" +) \ No newline at end of file diff --git a/Resources/UserScriptsData/NativeScriptTemplate.cpp b/Resources/UserScriptsData/NativeScriptTemplate.cpp new file mode 100644 index 0000000..d266a28 --- /dev/null +++ b/Resources/UserScriptsData/NativeScriptTemplate.cpp @@ -0,0 +1,16 @@ +#include "NativeScriptTemplate.h" + +void PLACE_HOLDER::Awake() +{ + +} + +void PLACE_HOLDER::OnDestroy() +{ + +} + +void PLACE_HOLDER::OnUpdate(double DeltaTime) +{ + +} \ No newline at end of file diff --git a/Resources/UserScriptsData/NativeScriptTemplate.h b/Resources/UserScriptsData/NativeScriptTemplate.h new file mode 100644 index 0000000..216081b --- /dev/null +++ b/Resources/UserScriptsData/NativeScriptTemplate.h @@ -0,0 +1,19 @@ +#include "SubSystems/FocalEngine/Resources/UserScriptsData/FENativeScriptConnector.h" +using namespace FocalEngine; + +// DO NOT CHANGE THIS LINE. +SET_MODULE_ID("PLACE_HOLDER"); + +class PLACE_HOLDER : public FENativeScriptCore +{ +public: + void Awake() override; + void OnUpdate(double DeltaTime) override; + void OnDestroy() override; + + int ExampleVariable = 0; +}; + +REGISTER_SCRIPT(PLACE_HOLDER) +// Way to expose variable to an editor. +REGISTER_SCRIPT_FIELD(PLACE_HOLDER, int, ExampleVariable) diff --git a/SubSystems/FEBasicApplication b/SubSystems/FEBasicApplication index 44d9d73..a87c8c1 160000 --- a/SubSystems/FEBasicApplication +++ b/SubSystems/FEBasicApplication @@ -1 +1 @@ -Subproject commit 44d9d73d6cfdf422922de6e96e83863210205260 +Subproject commit a87c8c1eab5f5f71f4cdf55221ec69e05d820f5f diff --git a/SubSystems/FEBasicCamera.cpp b/SubSystems/FEBasicCamera.cpp deleted file mode 100644 index cd304d8..0000000 --- a/SubSystems/FEBasicCamera.cpp +++ /dev/null @@ -1,332 +0,0 @@ -#include "FEBasicCamera.h" -using namespace FocalEngine; - -FEBasicCamera::FEBasicCamera(const std::string Name) : FEObject(FE_CAMERA, Name) -{ - Yaw = 0.0f; - UpdateAll(); - - Frustum = new float*[6]; - for (size_t i = 0; i < 6; i++) - { - Frustum[i] = new float[4]; - } -} - -FEBasicCamera::~FEBasicCamera() -{ - for (size_t i = 0; i < 6; i++) - { - delete[] Frustum[i]; - } - delete[] Frustum; -} - -float FEBasicCamera::GetYaw() -{ - return Yaw; -} - -void FEBasicCamera::SetYaw(const float NewYaw) -{ - Yaw = NewYaw; - UpdateViewMatrix(); -} - -float FEBasicCamera::GetPitch() -{ - return Pitch; -} - -void FEBasicCamera::SetPitch(const float NewPitch) -{ - Pitch = NewPitch; - UpdateViewMatrix(); -} - -float FEBasicCamera::GetRoll() -{ - return Roll; -} - -void FEBasicCamera::SetRoll(const float NewRoll) -{ - Roll = NewRoll; - UpdateViewMatrix(); -} - -glm::vec3 FEBasicCamera::GetPosition() const -{ - return Position; -} - -void FEBasicCamera::SetPosition(const glm::vec3 NewPosition) -{ - Position = NewPosition; - UpdateViewMatrix(); -} - -float FEBasicCamera::GetAspectRatio() const -{ - return AspectRatio; -} - -void FEBasicCamera::SetAspectRatio(const float NewAspectRatio) -{ - AspectRatio = NewAspectRatio; - UpdateProjectionMatrix(); -} - -void FEBasicCamera::UpdateViewMatrix() -{ - ViewMatrix = glm::mat4(1.0f); - - ViewMatrix = glm::rotate(ViewMatrix, GetPitch() * ANGLE_TORADIANS_COF, glm::vec3(1, 0, 0)); - ViewMatrix = glm::rotate(ViewMatrix, GetYaw() * ANGLE_TORADIANS_COF, glm::vec3(0, 1, 0)); - ViewMatrix = glm::rotate(ViewMatrix, GetRoll() * ANGLE_TORADIANS_COF, glm::vec3(0, 0, 1)); - - const glm::vec3 CameraPosition = GetPosition(); - const glm::vec3 NegativeCameraPosition = -CameraPosition; - - ViewMatrix = glm::translate(ViewMatrix, NegativeCameraPosition); -} - -glm::mat4 FEBasicCamera::GetViewMatrix() const -{ - return ViewMatrix; -} - -glm::mat4 FEBasicCamera::GetProjectionMatrix() const -{ - return ProjectionMatrix; -} - -void FEBasicCamera::UpdateProjectionMatrix() -{ - ProjectionMatrix = glm::perspective(Fov, AspectRatio, NearPlane, FarPlane); -} - -void FEBasicCamera::UpdateAll() -{ - UpdateViewMatrix(); - UpdateProjectionMatrix(); -} - -float FEBasicCamera::GetFov() const -{ - return Fov; -} - -void FEBasicCamera::SetFov(const float NewFov) -{ - Fov = NewFov; - UpdateProjectionMatrix(); -} - -float FEBasicCamera::GetNearPlane() const -{ - return NearPlane; -} - -void FEBasicCamera::SetNearPlane(const float NewNearPlane) -{ - NearPlane = NewNearPlane; - UpdateProjectionMatrix(); -} - -float FEBasicCamera::GetFarPlane() const -{ - return FarPlane; -} - -void FEBasicCamera::SetFarPlane(const float NewFarPlane) -{ - FarPlane = NewFarPlane; - UpdateProjectionMatrix(); -} - -float FEBasicCamera::GetGamma() const -{ - return Gamma; -} - -void FEBasicCamera::SetGamma(const float NewGamma) -{ - Gamma = NewGamma; -} - -float FEBasicCamera::GetExposure() const -{ - return Exposure; -} - -void FEBasicCamera::SetExposure(const float NewExposure) -{ - Exposure = NewExposure; -} - -bool FEBasicCamera::GetIsInputActive() -{ - return bIsInputActive; -} - -void FEBasicCamera::SetIsInputActive(const bool bIsActive) -{ - bIsInputActive = bIsActive; -} - -void FocalEngine::FEBasicCamera::Reset() -{ - Fov = 70.0f; - NearPlane = 0.1f; - FarPlane = 5000.0f; - AspectRatio = 1.0f; - - Yaw = 0.0f; - Pitch = 0.0f; - Roll = 0.0f; - - Gamma = 2.2f; - Exposure = 1.0f; - - Position = glm::vec3(0.0f); - - UpdateAll(); -} - -void FEBasicCamera::SetLookAt(const glm::vec3 LookAt) -{ - ViewMatrix = glm::lookAt(GetPosition(), LookAt, glm::vec3(0.0, 1.0, 0.0)); -} - -glm::vec3 FEBasicCamera::GetForward() -{ - return glm::normalize(glm::vec3(glm::vec4(0.0f, 0.0f, -1.0f, 0.0f) * ViewMatrix)); -} - -void FEBasicCamera::SetOnUpdate(void(*Func)(FEBasicCamera*)) -{ - ClientOnUpdateImpl = Func; -} - -glm::vec3 FEBasicCamera::GetRight() -{ - return glm::normalize(glm::vec3(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f) * ViewMatrix)); -} - -glm::vec3 FEBasicCamera::GetUp() -{ - return glm::normalize(glm::vec3(glm::vec4(0.0f, 1.0f, 0.0f, 0.0f) * ViewMatrix)); -} - -void FEBasicCamera::UpdateFrustumPlanes() -{ - float Clip[16]; - - glm::mat4 Cliping = GetProjectionMatrix() * GetViewMatrix(); - for (int i = 0; i < 4; i++) - { - Clip[i * 4] = Cliping[i][0]; - Clip[i * 4 + 1] = Cliping[i][1]; - Clip[i * 4 + 2] = Cliping[i][2]; - Clip[i * 4 + 3] = Cliping[i][3]; - } - - /* Extract the numbers for the RIGHT plane */ - Frustum[0][0] = Clip[3] - Clip[0]; - Frustum[0][1] = Clip[7] - Clip[4]; - Frustum[0][2] = Clip[11] - Clip[8]; - Frustum[0][3] = Clip[15] - Clip[12]; - - /* Normalize the result */ - float T = sqrt(Frustum[0][0] * Frustum[0][0] + Frustum[0][1] * Frustum[0][1] + Frustum[0][2] * Frustum[0][2]); - Frustum[0][0] /= T; - Frustum[0][1] /= T; - Frustum[0][2] /= T; - Frustum[0][3] /= T; - - /* Extract the numbers for the LEFT plane */ - Frustum[1][0] = Clip[3] + Clip[0]; - Frustum[1][1] = Clip[7] + Clip[4]; - Frustum[1][2] = Clip[11] + Clip[8]; - Frustum[1][3] = Clip[15] + Clip[12]; - - /* Normalize the result */ - T = sqrt(Frustum[1][0] * Frustum[1][0] + Frustum[1][1] * Frustum[1][1] + Frustum[1][2] * Frustum[1][2]); - Frustum[1][0] /= T; - Frustum[1][1] /= T; - Frustum[1][2] /= T; - Frustum[1][3] /= T; - - /* Extract the BOTTOM plane */ - Frustum[2][0] = Clip[3] + Clip[1]; - Frustum[2][1] = Clip[7] + Clip[5]; - Frustum[2][2] = Clip[11] + Clip[9]; - Frustum[2][3] = Clip[15] + Clip[13]; - - /* Normalize the result */ - T = sqrt(Frustum[2][0] * Frustum[2][0] + Frustum[2][1] * Frustum[2][1] + Frustum[2][2] * Frustum[2][2]); - Frustum[2][0] /= T; - Frustum[2][1] /= T; - Frustum[2][2] /= T; - Frustum[2][3] /= T; - - /* Extract the TOP plane */ - Frustum[3][0] = Clip[3] - Clip[1]; - Frustum[3][1] = Clip[7] - Clip[5]; - Frustum[3][2] = Clip[11] - Clip[9]; - Frustum[3][3] = Clip[15] - Clip[13]; - - /* Normalize the result */ - T = sqrt(Frustum[3][0] * Frustum[3][0] + Frustum[3][1] * Frustum[3][1] + Frustum[3][2] * Frustum[3][2]); - Frustum[3][0] /= T; - Frustum[3][1] /= T; - Frustum[3][2] /= T; - Frustum[3][3] /= T; - - /* Extract the FAR plane */ - Frustum[4][0] = Clip[3] - Clip[2]; - Frustum[4][1] = Clip[7] - Clip[6]; - Frustum[4][2] = Clip[11] - Clip[10]; - Frustum[4][3] = Clip[15] - Clip[14]; - - /* Normalize the result */ - T = sqrt(Frustum[4][0] * Frustum[4][0] + Frustum[4][1] * Frustum[4][1] + Frustum[4][2] * Frustum[4][2]); - Frustum[4][0] /= T; - Frustum[4][1] /= T; - Frustum[4][2] /= T; - Frustum[4][3] /= T; - - /* Extract the NEAR plane */ - Frustum[5][0] = Clip[3] + Clip[2]; - Frustum[5][1] = Clip[7] + Clip[6]; - Frustum[5][2] = Clip[11] + Clip[10]; - Frustum[5][3] = Clip[15] + Clip[14]; - - /* Normalize the result */ - T = sqrt(Frustum[5][0] * Frustum[5][0] + Frustum[5][1] * Frustum[5][1] + Frustum[5][2] * Frustum[5][2]); - Frustum[5][0] /= T; - Frustum[5][1] /= T; - Frustum[5][2] /= T; - Frustum[5][3] /= T; -} - -float** FEBasicCamera::GetFrustumPlanes() -{ - return Frustum; -} - -float FEBasicCamera::GetMovementSpeed() -{ - return MovementSpeed; -} - -void FEBasicCamera::SetMovementSpeed(const float NewValue) -{ - MovementSpeed = NewValue; -} - -int FEBasicCamera::GetCameraType() const -{ - return Type; -} \ No newline at end of file diff --git a/SubSystems/FEBasicCamera.h b/SubSystems/FEBasicCamera.h deleted file mode 100644 index 2ec4f15..0000000 --- a/SubSystems/FEBasicCamera.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -#include "Scene/FETransformComponent.h" - -namespace FocalEngine -{ - class FERenderer; - - class FEBasicCamera : public FEObject - { - friend FERenderer; - friend class FEOpenXRRendering; - public: - FEBasicCamera(std::string Name); - ~FEBasicCamera(); - - float GetFov() const; - void SetFov(float NewFov); - float GetNearPlane() const; - void SetNearPlane(float NewNearPlane); - float GetFarPlane() const; - void SetFarPlane(float NewFarPlane); - - virtual float GetYaw(); - virtual void SetYaw(float NewYaw); - virtual float GetPitch(); - virtual void SetPitch(float NewPitch); - virtual float GetRoll(); - virtual void SetRoll(float NewRoll); - glm::vec3 GetPosition() const; - void SetPosition(glm::vec3 NewPosition); - virtual glm::vec3 GetUp(); - virtual glm::vec3 GetForward(); - virtual glm::vec3 GetRight(); - - virtual void SetLookAt(glm::vec3 LookAt); - - float GetAspectRatio() const; - void SetAspectRatio(float NewAspectRatio); - - float GetGamma() const; - void SetGamma(float NewGamma); - float GetExposure() const; - void SetExposure(float NewExposure); - - glm::mat4 GetViewMatrix() const; - virtual void UpdateViewMatrix(); - glm::mat4 GetProjectionMatrix() const; - virtual void UpdateProjectionMatrix(); - - virtual void UpdateAll(); - - virtual void Move(float DeltaTime = 0.0f) {}; - - virtual bool GetIsInputActive(); - virtual void SetIsInputActive(bool bIsActive); - - virtual void KeyboardInput(int Key, int Scancode, int Action, int Mods) {}; - virtual void MouseMoveInput(double Xpos, double Ypos) {}; - virtual void MouseScrollInput(double Xoffset, double Yoffset) {}; - - virtual void Reset(); - - virtual void SetOnUpdate(void(*Func)(FEBasicCamera*)); - - virtual void UpdateFrustumPlanes(); - virtual float** GetFrustumPlanes(); - - virtual float GetMovementSpeed(); - virtual void SetMovementSpeed(float NewValue); - - int GetCameraType() const; - protected: - bool bIsInputActive = true; - - float Fov = 70.0f; - float NearPlane = 0.1f; - float FarPlane = 15000.0f; - float AspectRatio = 1.0f; - - float Yaw = 0.0f; - float Pitch = 0.0f; - float Roll = 0.0f; - - float Gamma = 2.2f; - float Exposure = 1.0f; - - float MovementSpeed = 10.0f; - - glm::vec3 Position = glm::vec3(0.0f); - glm::mat4 ViewMatrix; - glm::mat4 ProjectionMatrix; - - void(*ClientOnUpdateImpl)(FEBasicCamera*) = nullptr; - - float** Frustum; - - int Type = 0; - }; -} \ No newline at end of file diff --git a/SubSystems/FEInput.cpp b/SubSystems/FEInput.cpp new file mode 100644 index 0000000..d81c656 --- /dev/null +++ b/SubSystems/FEInput.cpp @@ -0,0 +1,352 @@ +#include "FEInput.h" +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetInput() +{ + return FEInput::GetInstancePointer(); +} +#endif + +FEInput::FEInput() +{ + APPLICATION.GetMainWindow()->AddOnMouseButtonCallback(&FEInput::MouseButtonCallback); + APPLICATION.GetMainWindow()->AddOnMouseMoveCallback(&FEInput::MouseMoveCallback); + APPLICATION.GetMainWindow()->AddOnKeyCallback(&FEInput::KeyButtonCallback); + APPLICATION.GetMainWindow()->AddOnScrollCallback(&FEInput::MouseScrollCallback); + + KeyMap[FEInputKey::FE_KEY_UNKNOWN] = FEInputKeyInfo(FEInputKey::FE_KEY_UNKNOWN); + KeyMap[FEInputKey::FE_KEY_SPACE] = FEInputKeyInfo(FEInputKey::FE_KEY_SPACE); + KeyMap[FEInputKey::FE_KEY_APOSTROPHE] = FEInputKeyInfo(FEInputKey::FE_KEY_APOSTROPHE); + KeyMap[FEInputKey::FE_KEY_COMMA] = FEInputKeyInfo(FEInputKey::FE_KEY_COMMA); + KeyMap[FEInputKey::FE_KEY_MINUS] = FEInputKeyInfo(FEInputKey::FE_KEY_MINUS); + KeyMap[FEInputKey::FE_KEY_PERIOD] = FEInputKeyInfo(FEInputKey::FE_KEY_PERIOD); + KeyMap[FEInputKey::FE_KEY_SLASH] = FEInputKeyInfo(FEInputKey::FE_KEY_SLASH); + KeyMap[FEInputKey::FE_KEY_0] = FEInputKeyInfo(FEInputKey::FE_KEY_0); + KeyMap[FEInputKey::FE_KEY_1] = FEInputKeyInfo(FEInputKey::FE_KEY_1); + KeyMap[FEInputKey::FE_KEY_2] = FEInputKeyInfo(FEInputKey::FE_KEY_2); + KeyMap[FEInputKey::FE_KEY_3] = FEInputKeyInfo(FEInputKey::FE_KEY_3); + KeyMap[FEInputKey::FE_KEY_4] = FEInputKeyInfo(FEInputKey::FE_KEY_4); + KeyMap[FEInputKey::FE_KEY_5] = FEInputKeyInfo(FEInputKey::FE_KEY_5); + KeyMap[FEInputKey::FE_KEY_6] = FEInputKeyInfo(FEInputKey::FE_KEY_6); + KeyMap[FEInputKey::FE_KEY_7] = FEInputKeyInfo(FEInputKey::FE_KEY_7); + KeyMap[FEInputKey::FE_KEY_8] = FEInputKeyInfo(FEInputKey::FE_KEY_8); + KeyMap[FEInputKey::FE_KEY_9] = FEInputKeyInfo(FEInputKey::FE_KEY_9); + KeyMap[FEInputKey::FE_KEY_SEMICOLON] = FEInputKeyInfo(FEInputKey::FE_KEY_SEMICOLON); + KeyMap[FEInputKey::FE_KEY_EQUAL] = FEInputKeyInfo(FEInputKey::FE_KEY_EQUAL); + KeyMap[FEInputKey::FE_KEY_A] = FEInputKeyInfo(FEInputKey::FE_KEY_A); + KeyMap[FEInputKey::FE_KEY_B] = FEInputKeyInfo(FEInputKey::FE_KEY_B); + KeyMap[FEInputKey::FE_KEY_C] = FEInputKeyInfo(FEInputKey::FE_KEY_C); + KeyMap[FEInputKey::FE_KEY_D] = FEInputKeyInfo(FEInputKey::FE_KEY_D); + KeyMap[FEInputKey::FE_KEY_E] = FEInputKeyInfo(FEInputKey::FE_KEY_E); + KeyMap[FEInputKey::FE_KEY_F] = FEInputKeyInfo(FEInputKey::FE_KEY_F); + KeyMap[FEInputKey::FE_KEY_G] = FEInputKeyInfo(FEInputKey::FE_KEY_G); + KeyMap[FEInputKey::FE_KEY_H] = FEInputKeyInfo(FEInputKey::FE_KEY_H); + KeyMap[FEInputKey::FE_KEY_I] = FEInputKeyInfo(FEInputKey::FE_KEY_I); + KeyMap[FEInputKey::FE_KEY_J] = FEInputKeyInfo(FEInputKey::FE_KEY_J); + KeyMap[FEInputKey::FE_KEY_K] = FEInputKeyInfo(FEInputKey::FE_KEY_K); + KeyMap[FEInputKey::FE_KEY_L] = FEInputKeyInfo(FEInputKey::FE_KEY_L); + KeyMap[FEInputKey::FE_KEY_M] = FEInputKeyInfo(FEInputKey::FE_KEY_M); + KeyMap[FEInputKey::FE_KEY_N] = FEInputKeyInfo(FEInputKey::FE_KEY_N); + KeyMap[FEInputKey::FE_KEY_O] = FEInputKeyInfo(FEInputKey::FE_KEY_O); + KeyMap[FEInputKey::FE_KEY_P] = FEInputKeyInfo(FEInputKey::FE_KEY_P); + KeyMap[FEInputKey::FE_KEY_Q] = FEInputKeyInfo(FEInputKey::FE_KEY_Q); + KeyMap[FEInputKey::FE_KEY_R] = FEInputKeyInfo(FEInputKey::FE_KEY_R); + KeyMap[FEInputKey::FE_KEY_S] = FEInputKeyInfo(FEInputKey::FE_KEY_S); + KeyMap[FEInputKey::FE_KEY_T] = FEInputKeyInfo(FEInputKey::FE_KEY_T); + KeyMap[FEInputKey::FE_KEY_U] = FEInputKeyInfo(FEInputKey::FE_KEY_U); + KeyMap[FEInputKey::FE_KEY_V] = FEInputKeyInfo(FEInputKey::FE_KEY_V); + KeyMap[FEInputKey::FE_KEY_W] = FEInputKeyInfo(FEInputKey::FE_KEY_W); + KeyMap[FEInputKey::FE_KEY_X] = FEInputKeyInfo(FEInputKey::FE_KEY_X); + KeyMap[FEInputKey::FE_KEY_Y] = FEInputKeyInfo(FEInputKey::FE_KEY_Y); + KeyMap[FEInputKey::FE_KEY_Z] = FEInputKeyInfo(FEInputKey::FE_KEY_Z); + KeyMap[FEInputKey::FE_KEY_LEFT_BRACKET] = FEInputKeyInfo(FEInputKey::FE_KEY_LEFT_BRACKET); + KeyMap[FEInputKey::FE_KEY_BACKSLASH] = FEInputKeyInfo(FEInputKey::FE_KEY_BACKSLASH); + KeyMap[FEInputKey::FE_KEY_RIGHT_BRACKET] = FEInputKeyInfo(FEInputKey::FE_KEY_RIGHT_BRACKET); + KeyMap[FEInputKey::FE_KEY_GRAVE_ACCENT] = FEInputKeyInfo(FEInputKey::FE_KEY_GRAVE_ACCENT); + KeyMap[FEInputKey::FE_KEY_WORLD_1] = FEInputKeyInfo(FEInputKey::FE_KEY_WORLD_1); + KeyMap[FEInputKey::FE_KEY_WORLD_2] = FEInputKeyInfo(FEInputKey::FE_KEY_WORLD_2); + + KeyMap[FEInputKey::FE_KEY_ESCAPE] = FEInputKeyInfo(FEInputKey::FE_KEY_ESCAPE); + KeyMap[FEInputKey::FE_KEY_ENTER] = FEInputKeyInfo(FEInputKey::FE_KEY_ENTER); + KeyMap[FEInputKey::FE_KEY_TAB] = FEInputKeyInfo(FEInputKey::FE_KEY_TAB); + KeyMap[FEInputKey::FE_KEY_BACKSPACE] = FEInputKeyInfo(FEInputKey::FE_KEY_BACKSPACE); + KeyMap[FEInputKey::FE_KEY_INSERT] = FEInputKeyInfo(FEInputKey::FE_KEY_INSERT); + KeyMap[FEInputKey::FE_KEY_DELETE] = FEInputKeyInfo(FEInputKey::FE_KEY_DELETE); + KeyMap[FEInputKey::FE_KEY_RIGHT] = FEInputKeyInfo(FEInputKey::FE_KEY_RIGHT); + KeyMap[FEInputKey::FE_KEY_LEFT] = FEInputKeyInfo(FEInputKey::FE_KEY_LEFT); + KeyMap[FEInputKey::FE_KEY_DOWN] = FEInputKeyInfo(FEInputKey::FE_KEY_DOWN); + KeyMap[FEInputKey::FE_KEY_UP] = FEInputKeyInfo(FEInputKey::FE_KEY_UP); + KeyMap[FEInputKey::FE_KEY_PAGE_UP] = FEInputKeyInfo(FEInputKey::FE_KEY_PAGE_UP); + KeyMap[FEInputKey::FE_KEY_PAGE_DOWN] = FEInputKeyInfo(FEInputKey::FE_KEY_PAGE_DOWN); + KeyMap[FEInputKey::FE_KEY_HOME] = FEInputKeyInfo(FEInputKey::FE_KEY_HOME); + KeyMap[FEInputKey::FE_KEY_END] = FEInputKeyInfo(FEInputKey::FE_KEY_END); + KeyMap[FEInputKey::FE_KEY_CAPS_LOCK] = FEInputKeyInfo(FEInputKey::FE_KEY_CAPS_LOCK); + KeyMap[FEInputKey::FE_KEY_SCROLL_LOCK] = FEInputKeyInfo(FEInputKey::FE_KEY_SCROLL_LOCK); + KeyMap[FEInputKey::FE_KEY_NUM_LOCK] = FEInputKeyInfo(FEInputKey::FE_KEY_NUM_LOCK); + KeyMap[FEInputKey::FE_KEY_PRINT_SCREEN] = FEInputKeyInfo(FEInputKey::FE_KEY_PRINT_SCREEN); + KeyMap[FEInputKey::FE_KEY_PAUSE] = FEInputKeyInfo(FEInputKey::FE_KEY_PAUSE); + KeyMap[FEInputKey::FE_KEY_F1] = FEInputKeyInfo(FEInputKey::FE_KEY_F1); + KeyMap[FEInputKey::FE_KEY_F2] = FEInputKeyInfo(FEInputKey::FE_KEY_F2); + KeyMap[FEInputKey::FE_KEY_F3] = FEInputKeyInfo(FEInputKey::FE_KEY_F3); + KeyMap[FEInputKey::FE_KEY_F4] = FEInputKeyInfo(FEInputKey::FE_KEY_F4); + KeyMap[FEInputKey::FE_KEY_F5] = FEInputKeyInfo(FEInputKey::FE_KEY_F5); + KeyMap[FEInputKey::FE_KEY_F6] = FEInputKeyInfo(FEInputKey::FE_KEY_F6); + KeyMap[FEInputKey::FE_KEY_F7] = FEInputKeyInfo(FEInputKey::FE_KEY_F7); + KeyMap[FEInputKey::FE_KEY_F8] = FEInputKeyInfo(FEInputKey::FE_KEY_F8); + KeyMap[FEInputKey::FE_KEY_F9] = FEInputKeyInfo(FEInputKey::FE_KEY_F9); + KeyMap[FEInputKey::FE_KEY_F10] = FEInputKeyInfo(FEInputKey::FE_KEY_F10); + KeyMap[FEInputKey::FE_KEY_F11] = FEInputKeyInfo(FEInputKey::FE_KEY_F11); + KeyMap[FEInputKey::FE_KEY_F12] = FEInputKeyInfo(FEInputKey::FE_KEY_F12); + KeyMap[FEInputKey::FE_KEY_F13] = FEInputKeyInfo(FEInputKey::FE_KEY_F13); + KeyMap[FEInputKey::FE_KEY_F14] = FEInputKeyInfo(FEInputKey::FE_KEY_F14); + KeyMap[FEInputKey::FE_KEY_F15] = FEInputKeyInfo(FEInputKey::FE_KEY_F15); + KeyMap[FEInputKey::FE_KEY_F16] = FEInputKeyInfo(FEInputKey::FE_KEY_F16); + KeyMap[FEInputKey::FE_KEY_F17] = FEInputKeyInfo(FEInputKey::FE_KEY_F17); + KeyMap[FEInputKey::FE_KEY_F18] = FEInputKeyInfo(FEInputKey::FE_KEY_F18); + KeyMap[FEInputKey::FE_KEY_F19] = FEInputKeyInfo(FEInputKey::FE_KEY_F19); + KeyMap[FEInputKey::FE_KEY_F20] = FEInputKeyInfo(FEInputKey::FE_KEY_F20); + KeyMap[FEInputKey::FE_KEY_F21] = FEInputKeyInfo(FEInputKey::FE_KEY_F21); + KeyMap[FEInputKey::FE_KEY_F22] = FEInputKeyInfo(FEInputKey::FE_KEY_F22); + KeyMap[FEInputKey::FE_KEY_F23] = FEInputKeyInfo(FEInputKey::FE_KEY_F23); + KeyMap[FEInputKey::FE_KEY_F24] = FEInputKeyInfo(FEInputKey::FE_KEY_F24); + KeyMap[FEInputKey::FE_KEY_F25] = FEInputKeyInfo(FEInputKey::FE_KEY_F25); + KeyMap[FEInputKey::FE_KEY_KP_0] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_0); + KeyMap[FEInputKey::FE_KEY_KP_1] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_1); + KeyMap[FEInputKey::FE_KEY_KP_2] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_2); + KeyMap[FEInputKey::FE_KEY_KP_3] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_3); + KeyMap[FEInputKey::FE_KEY_KP_4] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_4); + KeyMap[FEInputKey::FE_KEY_KP_5] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_5); + KeyMap[FEInputKey::FE_KEY_KP_6] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_6); + KeyMap[FEInputKey::FE_KEY_KP_7] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_7); + KeyMap[FEInputKey::FE_KEY_KP_8] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_8); + KeyMap[FEInputKey::FE_KEY_KP_9] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_9); + KeyMap[FEInputKey::FE_KEY_KP_DECIMAL] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_DECIMAL); + KeyMap[FEInputKey::FE_KEY_KP_DIVIDE] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_DIVIDE); + KeyMap[FEInputKey::FE_KEY_KP_MULTIPLY] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_MULTIPLY); + KeyMap[FEInputKey::FE_KEY_KP_SUBTRACT] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_SUBTRACT); + KeyMap[FEInputKey::FE_KEY_KP_ADD] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_ADD); + KeyMap[FEInputKey::FE_KEY_KP_ENTER] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_ENTER); + KeyMap[FEInputKey::FE_KEY_KP_EQUAL] = FEInputKeyInfo(FEInputKey::FE_KEY_KP_EQUAL); + KeyMap[FEInputKey::FE_KEY_LEFT_SHIFT] = FEInputKeyInfo(FEInputKey::FE_KEY_LEFT_SHIFT); + KeyMap[FEInputKey::FE_KEY_LEFT_CONTROL] = FEInputKeyInfo(FEInputKey::FE_KEY_LEFT_CONTROL); + KeyMap[FEInputKey::FE_KEY_LEFT_ALT] = FEInputKeyInfo(FEInputKey::FE_KEY_LEFT_ALT); + KeyMap[FEInputKey::FE_KEY_LEFT_SUPER] = FEInputKeyInfo(FEInputKey::FE_KEY_LEFT_SUPER); + KeyMap[FEInputKey::FE_KEY_RIGHT_SHIFT] = FEInputKeyInfo(FEInputKey::FE_KEY_RIGHT_SHIFT); + KeyMap[FEInputKey::FE_KEY_RIGHT_CONTROL] = FEInputKeyInfo(FEInputKey::FE_KEY_RIGHT_CONTROL); + KeyMap[FEInputKey::FE_KEY_RIGHT_ALT] = FEInputKeyInfo(FEInputKey::FE_KEY_RIGHT_ALT); + KeyMap[FEInputKey::FE_KEY_RIGHT_SUPER] = FEInputKeyInfo(FEInputKey::FE_KEY_RIGHT_SUPER); + KeyMap[FEInputKey::FE_KEY_MENU] = FEInputKeyInfo(FEInputKey::FE_KEY_MENU); + + MouseState.Buttons[FEInputMouseButton::FE_MOUSE_BUTTON_1].State = FE_RELEASED; + MouseState.Buttons[FEInputMouseButton::FE_MOUSE_BUTTON_2].State = FE_RELEASED; + MouseState.Buttons[FEInputMouseButton::FE_MOUSE_BUTTON_3].State = FE_RELEASED; + MouseState.Buttons[FEInputMouseButton::FE_MOUSE_BUTTON_4].State = FE_RELEASED; + MouseState.Buttons[FEInputMouseButton::FE_MOUSE_BUTTON_5].State = FE_RELEASED; + MouseState.Buttons[FEInputMouseButton::FE_MOUSE_BUTTON_6].State = FE_RELEASED; + MouseState.Buttons[FEInputMouseButton::FE_MOUSE_BUTTON_7].State = FE_RELEASED; + MouseState.Buttons[FEInputMouseButton::FE_MOUSE_BUTTON_8].State = FE_RELEASED; +} + +FEInput::~FEInput() +{ +} + +void FEInput::Update() +{ + MouseState.ScrollXOffset = 0.0; + MouseState.ScrollYOffset = 0.0; + + KeyModifier = FEInputKeyModifier::FE_MOD_NONE; +} + +std::string FEInput::AddKeyCallback(std::function UserOnKeyButtonCallback) +{ + if (UserOnKeyButtonCallback == nullptr) + { + LOG.Add("UserOnKeyButtonCallback is nullptr", "FE_INPUT", FE_LOG_WARNING); + return ""; + } + + std::pair NewCallback = std::make_pair(UNIQUE_ID.GetUniqueHexID(), UserOnKeyButtonCallback); + UserOnKeyButtonCallbackFuncs.push_back(NewCallback); + + return NewCallback.first; +} + +std::string FEInput::AddMouseButtonCallback(std::function UserOnMouseButtonCallback) +{ + if (UserOnMouseButtonCallback == nullptr) + { + LOG.Add("UserOnMouseButtonCallback is nullptr", "FE_INPUT", FE_LOG_WARNING); + return ""; + } + + std::pair NewCallback = std::make_pair(UNIQUE_ID.GetUniqueHexID(), UserOnMouseButtonCallback); + UserOnMouseButtonCallbackFuncs.push_back(NewCallback); + + return NewCallback.first; +} + +std::string FEInput::AddMouseMoveCallback(std::function UserOnMouseMoveCallback) +{ + if (UserOnMouseMoveCallback == nullptr) + { + LOG.Add("UserOnMouseMoveCallback is nullptr", "FE_INPUT", FE_LOG_WARNING); + return ""; + } + + std::pair NewCallback = std::make_pair(UNIQUE_ID.GetUniqueHexID(), UserOnMouseMoveCallback); + UserOnMouseMoveCallbackFuncs.push_back(NewCallback); + + return NewCallback.first; +} + +void FEInput::MouseButtonCallback(const int Button, const int Action, const int Mods) +{ + FEInputMouseButton TempButton = static_cast(Button); + INPUT.MouseState.Buttons[TempButton].State = static_cast(Action); + + for (size_t i = 0; i < INPUT.UserOnMouseButtonCallbackFuncs.size(); i++) + { + if (INPUT.UserOnMouseButtonCallbackFuncs[i].second == nullptr) + continue; + + INPUT.UserOnMouseButtonCallbackFuncs[i].second(Button, Action, Mods); + } +} + +void FEInput::MouseMoveCallback(double Xpos, double Ypos) +{ + for (size_t i = 0; i < INPUT.UserOnMouseMoveCallbackFuncs.size(); i++) + { + if (INPUT.UserOnMouseMoveCallbackFuncs[i].second == nullptr) + continue; + + INPUT.UserOnMouseMoveCallbackFuncs[i].second(Xpos, Ypos); + } + + INPUT.MouseState.X = Xpos; + INPUT.MouseState.Y = Ypos; +} + +void FEInput::KeyButtonCallback(const int Key, const int Scancode, const int Action, const int Mods) +{ + FEInputKey TempKey = static_cast(Key); + + bool bWasChanged = INPUT.KeyMap[TempKey].State != static_cast(Action) || INPUT.KeyModifier != static_cast(Mods); + INPUT.KeyMap[TempKey].bLastFrameUpdated = bWasChanged; + + INPUT.KeyMap[TempKey].State = static_cast(Action); + INPUT.KeyModifier = static_cast(Mods); + + for (size_t i = 0; i < INPUT.UserOnKeyButtonCallbackFuncs.size(); i++) + { + if (INPUT.UserOnKeyButtonCallbackFuncs[i].second == nullptr) + continue; + + INPUT.UserOnKeyButtonCallbackFuncs[i].second(Key, Scancode, Action, Mods); + } +} + +void FEInput::MouseScrollCallback(const double Xoffset, const double Yoffset) +{ + INPUT.MouseState.ScrollXOffset = Xoffset; + INPUT.MouseState.ScrollYOffset = Yoffset; + + for (size_t i = 0; i < INPUT.ClientMouseScrollCallbacks.size(); i++) + { + if (INPUT.ClientMouseScrollCallbacks[i] == nullptr) + continue; + + INPUT.ClientMouseScrollCallbacks[i](Xoffset, Yoffset); + } +} + +void FEInput::SetMousePosition(int X, int Y, bool bScreenPosition) +{ + if (bScreenPosition) + { + glfwSetCursorPos(APPLICATION.GetMainWindow()->GetGlfwWindow(), X - APPLICATION.GetMainWindow()->GetXPosition(), Y - APPLICATION.GetMainWindow()->GetYPosition()); + X -= APPLICATION.GetMainWindow()->GetXPosition(); + Y -= APPLICATION.GetMainWindow()->GetYPosition(); + } + else + { + glfwSetCursorPos(APPLICATION.GetMainWindow()->GetGlfwWindow(), X, Y); + } + + INPUT.MouseState.X = X; + INPUT.MouseState.Y = Y; +} + +double FEInput::GetMouseX() +{ + return MouseState.X; +} + +double FEInput::GetMouseY() +{ + return MouseState.Y; +} + +void FEInput::EndFrame() +{ + auto KeyMapIterator = KeyMap.begin(); + while (KeyMapIterator != KeyMap.end()) + { + KeyMapIterator->second.bLastFrameUpdated = false; + KeyMapIterator++; + } +} + +FEInputKeyInfo FEInput::GetKeyInfo(FEInputKey Key) +{ + if (KeyMap.find(Key) == KeyMap.end()) + { + LOG.Add("Key with code: " + std::to_string(Key) + " not found in KeyMap", "FE_INPUT", FE_LOG_WARNING); + return FEInputKeyInfo(); + } + + return KeyMap[Key]; +} + +FEInputKeyModifier FEInput::GetKeyModifier() +{ + return KeyModifier; +} + +FEInputMouseState FEInput::GetMouseState() +{ + return MouseState; +} + +void FEInput::RemoveCallback(std::string CallbackID) +{ + for (int i = 0; i < UserOnMouseButtonCallbackFuncs.size(); i++) + { + if (UserOnMouseButtonCallbackFuncs[i].first == CallbackID) + { + UserOnMouseButtonCallbackFuncs.erase(UserOnMouseButtonCallbackFuncs.begin() + i); + return; + } + } + + for (int i = 0; i < UserOnMouseMoveCallbackFuncs.size(); i++) + { + if (UserOnMouseMoveCallbackFuncs[i].first == CallbackID) + { + UserOnMouseMoveCallbackFuncs.erase(UserOnMouseMoveCallbackFuncs.begin() + i); + return; + } + } + + for (int i = 0; i < UserOnKeyButtonCallbackFuncs.size(); i++) + { + if (UserOnKeyButtonCallbackFuncs[i].first == CallbackID) + { + UserOnKeyButtonCallbackFuncs.erase(UserOnKeyButtonCallbackFuncs.begin() + i); + return; + } + } +} \ No newline at end of file diff --git a/SubSystems/FEInput.h b/SubSystems/FEInput.h new file mode 100644 index 0000000..ff43c7f --- /dev/null +++ b/SubSystems/FEInput.h @@ -0,0 +1,255 @@ +#pragma once + +#include "../../Scene/FESceneManager.h" + +namespace FocalEngine +{ + // Internal representation of GLFW keys + enum FEInputKey + { + FE_KEY_UNKNOWN = -1, + FE_KEY_SPACE = 32, + FE_KEY_APOSTROPHE = 39, /* ' */ + FE_KEY_COMMA = 44, /* , */ + FE_KEY_MINUS = 45, /* - */ + FE_KEY_PERIOD = 46, /* . */ + FE_KEY_SLASH = 47, /* / */ + FE_KEY_0 = 48, + FE_KEY_1 = 49, + FE_KEY_2 = 50, + FE_KEY_3 = 51, + FE_KEY_4 = 52, + FE_KEY_5 = 53, + FE_KEY_6 = 54, + FE_KEY_7 = 55, + FE_KEY_8 = 56, + FE_KEY_9 = 57, + FE_KEY_SEMICOLON = 59, /* ; */ + FE_KEY_EQUAL = 61, /* = */ + FE_KEY_A = 65, + FE_KEY_B = 66, + FE_KEY_C = 67, + FE_KEY_D = 68, + FE_KEY_E = 69, + FE_KEY_F = 70, + FE_KEY_G = 71, + FE_KEY_H = 72, + FE_KEY_I = 73, + FE_KEY_J = 74, + FE_KEY_K = 75, + FE_KEY_L = 76, + FE_KEY_M = 77, + FE_KEY_N = 78, + FE_KEY_O = 79, + FE_KEY_P = 80, + FE_KEY_Q = 81, + FE_KEY_R = 82, + FE_KEY_S = 83, + FE_KEY_T = 84, + FE_KEY_U = 85, + FE_KEY_V = 86, + FE_KEY_W = 87, + FE_KEY_X = 88, + FE_KEY_Y = 89, + FE_KEY_Z = 90, + FE_KEY_LEFT_BRACKET = 91, /* [ */ + FE_KEY_BACKSLASH = 92, /* \ */ + FE_KEY_RIGHT_BRACKET = 93, /* ] */ + FE_KEY_GRAVE_ACCENT = 96, /* ` */ + FE_KEY_WORLD_1 = 161, /* non-US #1 */ + FE_KEY_WORLD_2 = 162, /* non-US #2 */ + + /* Function keys */ + FE_KEY_ESCAPE = 256, + FE_KEY_ENTER = 257, + FE_KEY_TAB = 258, + FE_KEY_BACKSPACE = 259, + FE_KEY_INSERT = 260, + FE_KEY_DELETE = 261, + FE_KEY_RIGHT = 262, + FE_KEY_LEFT = 263, + FE_KEY_DOWN = 264, + FE_KEY_UP = 265, + FE_KEY_PAGE_UP = 266, + FE_KEY_PAGE_DOWN = 267, + FE_KEY_HOME = 268, + FE_KEY_END = 269, + FE_KEY_CAPS_LOCK = 280, + FE_KEY_SCROLL_LOCK = 281, + FE_KEY_NUM_LOCK = 282, + FE_KEY_PRINT_SCREEN = 283, + FE_KEY_PAUSE = 284, + FE_KEY_F1 = 290, + FE_KEY_F2 = 291, + FE_KEY_F3 = 292, + FE_KEY_F4 = 293, + FE_KEY_F5 = 294, + FE_KEY_F6 = 295, + FE_KEY_F7 = 296, + FE_KEY_F8 = 297, + FE_KEY_F9 = 298, + FE_KEY_F10 = 299, + FE_KEY_F11 = 300, + FE_KEY_F12 = 301, + FE_KEY_F13 = 302, + FE_KEY_F14 = 303, + FE_KEY_F15 = 304, + FE_KEY_F16 = 305, + FE_KEY_F17 = 306, + FE_KEY_F18 = 307, + FE_KEY_F19 = 308, + FE_KEY_F20 = 309, + FE_KEY_F21 = 310, + FE_KEY_F22 = 311, + FE_KEY_F23 = 312, + FE_KEY_F24 = 313, + FE_KEY_F25 = 314, + FE_KEY_KP_0 = 320, + FE_KEY_KP_1 = 321, + FE_KEY_KP_2 = 322, + FE_KEY_KP_3 = 323, + FE_KEY_KP_4 = 324, + FE_KEY_KP_5 = 325, + FE_KEY_KP_6 = 326, + FE_KEY_KP_7 = 327, + FE_KEY_KP_8 = 328, + FE_KEY_KP_9 = 329, + FE_KEY_KP_DECIMAL = 330, + FE_KEY_KP_DIVIDE = 331, + FE_KEY_KP_MULTIPLY = 332, + FE_KEY_KP_SUBTRACT = 333, + FE_KEY_KP_ADD = 334, + FE_KEY_KP_ENTER = 335, + FE_KEY_KP_EQUAL = 336, + FE_KEY_LEFT_SHIFT = 340, + FE_KEY_LEFT_CONTROL = 341, + FE_KEY_LEFT_ALT = 342, + FE_KEY_LEFT_SUPER = 343, + FE_KEY_RIGHT_SHIFT = 344, + FE_KEY_RIGHT_CONTROL = 345, + FE_KEY_RIGHT_ALT = 346, + FE_KEY_RIGHT_SUPER = 347, + FE_KEY_MENU = 348 + }; + + enum FEInputKeyState + { + FE_RELEASED = 0, + FE_PRESSED = 1, + FE_REPEAT = 2 + }; + + enum FEInputKeyModifier + { + FE_MOD_NONE = 0x0000, + FE_MOD_SHIFT = 0x0001, + FE_MOD_CONTROL = 0x0002, + FE_MOD_ALT = 0x0004, + FE_MOD_SUPER = 0x0008, + FE_MOD_CAPS_LOCK = 0x0010, + FE_MOD_NUM_LOCK = 0x0020 + }; + + struct FEInputKeyInfo + { + FEInputKey Key; + FEInputKeyState State; + bool bLastFrameUpdated = false; + + FEInputKeyInfo() + { + this->Key = FE_KEY_UNKNOWN; + this->State = FE_RELEASED; + } + + FEInputKeyInfo(FEInputKey Key, FEInputKeyState State = FE_RELEASED) + { + this->Key = Key; + this->State = State; + } + }; + + enum FEInputMouseButton + { + FE_MOUSE_BUTTON_1 = 0, + FE_MOUSE_BUTTON_2 = 1, + FE_MOUSE_BUTTON_3 = 2, + FE_MOUSE_BUTTON_4 = 3, + FE_MOUSE_BUTTON_5 = 4, + FE_MOUSE_BUTTON_6 = 5, + FE_MOUSE_BUTTON_7 = 6, + FE_MOUSE_BUTTON_8 = 7, + FE_MOUSE_BUTTON_LAST = FE_MOUSE_BUTTON_8, + FE_MOUSE_BUTTON_LEFT = FE_MOUSE_BUTTON_1, + FE_MOUSE_BUTTON_RIGHT = FE_MOUSE_BUTTON_2, + FE_MOUSE_BUTTON_MIDDLE = FE_MOUSE_BUTTON_3 + }; + + struct FEInputMouseButtonState + { + FEInputMouseButton Button; + FEInputKeyState State; + }; + + struct FEInputMouseState + { + double X, Y; + double ScrollXOffset = 0.0, ScrollYOffset = 0.0; + + std::unordered_map Buttons; + }; + + class FOCAL_ENGINE_API FEInput + { + friend class FEngine; + public: + SINGLETON_PUBLIC_PART(FEInput) + + std::string AddKeyCallback(std::function UserOnKeyButtonCallback); + std::string AddMouseButtonCallback(std::function UserOnMouseButtonCallback); + std::string AddMouseMoveCallback(std::function UserOnMouseMoveCallback); + + // TO-DO: It is terrible to obligate the user to remove the callback or application will crash. We should remove it automatically. + void RemoveCallback(std::string CallbackID); + + void Update(); + + FEInputKeyInfo GetKeyInfo(FEInputKey Key); + FEInputKeyModifier GetKeyModifier(); + + FEInputMouseState GetMouseState(); + + double GetMouseX(); + double GetMouseY(); + + void SetMousePosition(int X, int Y, bool bScreenPosition = true); + private: + SINGLETON_PRIVATE_PART(FEInput) + + std::unordered_map KeyMap; + FEInputKeyModifier KeyModifier = FE_MOD_NONE; + + FEInputMouseState MouseState; + + static void MouseButtonCallback(int Button, int Action, int Mods); + std::vector>> UserOnMouseButtonCallbackFuncs; + + static void MouseMoveCallback(double Xpos, double Ypos); + std::vector>> UserOnMouseMoveCallbackFuncs; + + static void KeyButtonCallback(int Key, int Scancode, int Action, int Mods); + std::vector>> UserOnKeyButtonCallbackFuncs; + + static void MouseScrollCallback(double Xoffset, double Yoffset); + std::vector ClientMouseScrollCallbacks; + + void EndFrame(); + }; + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetInput(); + #define INPUT (*static_cast(GetInput())) +#else + #define INPUT FEInput::GetInstance() +#endif +} \ No newline at end of file diff --git a/SubSystems/FEOpenXR/FEOpenXR.cpp b/SubSystems/FEOpenXR/FEOpenXR.cpp index 9eee8f6..b0c71c6 100644 --- a/SubSystems/FEOpenXR/FEOpenXR.cpp +++ b/SubSystems/FEOpenXR/FEOpenXR.cpp @@ -2,7 +2,12 @@ using namespace FocalEngine; -FEOpenXR* FEOpenXR::Instance = nullptr; +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetOpenXR() +{ + return FEOpenXR::GetInstancePointer(); +} +#endif FEOpenXR::FEOpenXR() {} FEOpenXR::~FEOpenXR() @@ -86,9 +91,9 @@ void FEOpenXR::PollEvents() { case XR_SESSION_STATE_READY: { - XrSessionBeginInfo sessionBeginInfo{ XR_TYPE_SESSION_BEGIN_INFO }; - sessionBeginInfo.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; - FE_OPENXR_ERROR(xrBeginSession(FEOpenXR_CORE.Session, &sessionBeginInfo)); + XrSessionBeginInfo SessionBeginInfo{ XR_TYPE_SESSION_BEGIN_INFO }; + SessionBeginInfo.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + FE_OPENXR_ERROR(xrBeginSession(FEOpenXR_CORE.Session, &SessionBeginInfo)); //m_sessionRunning = true; break; } diff --git a/SubSystems/FEOpenXR/FEOpenXR.h b/SubSystems/FEOpenXR/FEOpenXR.h index 15f8cce..c88990c 100644 --- a/SubSystems/FEOpenXR/FEOpenXR.h +++ b/SubSystems/FEOpenXR/FEOpenXR.h @@ -20,5 +20,10 @@ namespace FocalEngine void PollEvents(); }; -#define OpenXR_MANAGER FEOpenXR::getInstance() +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetOpenXR(); + #define OpenXR_MANAGER (*static_cast(GetOpenXR())) +#else + #define OpenXR_MANAGER FEOpenXR::GetInstance() +#endif } \ No newline at end of file diff --git a/SubSystems/FEOpenXR/FEOpenXRCore.cpp b/SubSystems/FEOpenXR/FEOpenXRCore.cpp index f1a2e79..9692e42 100644 --- a/SubSystems/FEOpenXR/FEOpenXRCore.cpp +++ b/SubSystems/FEOpenXR/FEOpenXRCore.cpp @@ -2,7 +2,12 @@ using namespace FocalEngine; -FEOpenXRCore* FEOpenXRCore::Instance = nullptr; +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetOpenXRCore() +{ + return FEOpenXRCore::GetInstancePointer(); +} +#endif FEOpenXRCore::FEOpenXRCore() {} FEOpenXRCore::~FEOpenXRCore() {} @@ -56,12 +61,12 @@ void FEOpenXRCore::InitializeSession() FE_OPENXR_ERROR(pfnGetOpenGLGraphicsRequirementsKHR(OpenXRInstance, SystemID, &OpenglReqs)); - XrGraphicsBindingOpenGLWin32KHR graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR }; - graphicsBinding.hDC = wglGetCurrentDC(); - graphicsBinding.hGLRC = wglGetCurrentContext(); + XrGraphicsBindingOpenGLWin32KHR GraphicsBinding{ XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR }; + GraphicsBinding.hDC = wglGetCurrentDC(); + GraphicsBinding.hGLRC = wglGetCurrentContext(); XrSessionCreateInfo SessionCreateInfo = { XR_TYPE_SESSION_CREATE_INFO }; - SessionCreateInfo.next = &graphicsBinding; + SessionCreateInfo.next = &GraphicsBinding; SessionCreateInfo.systemId = SystemID; FE_OPENXR_ERROR(xrCreateSession(OpenXRInstance, &SessionCreateInfo, &Session)); diff --git a/SubSystems/FEOpenXR/FEOpenXRCore.h b/SubSystems/FEOpenXR/FEOpenXRCore.h index ec7b8f5..92e7935 100644 --- a/SubSystems/FEOpenXR/FEOpenXRCore.h +++ b/SubSystems/FEOpenXR/FEOpenXRCore.h @@ -141,23 +141,23 @@ namespace FocalEngine } } - static void LogXrError(XrResult res, const char* originator = nullptr, const char* sourceLocation = nullptr) + static void LogXrError(XrResult Result, const char* Originator = nullptr, const char* SourceLocation = nullptr) { std::string ErrorMsg = ""; - ErrorMsg += "OpenXR error: " + XrResultToString(res) + "\n"; - ErrorMsg += "OpenXR error code: " + std::to_string(res) + "\n"; - ErrorMsg += "Function: " + std::string(originator) + "\n"; - ErrorMsg += "Source location: " + std::string(sourceLocation) + "\n"; + ErrorMsg += "OpenXR error: " + XrResultToString(Result) + "\n"; + ErrorMsg += "OpenXR error code: " + std::to_string(Result) + "\n"; + ErrorMsg += "Function: " + std::string(Originator) + "\n"; + ErrorMsg += "Source location: " + std::string(SourceLocation) + "\n"; LOG.Add(ErrorMsg, "FE_LOG_OPENXR"); } - static XrResult CheckXrResult(XrResult res, const char* originator = nullptr, const char* sourceLocation = nullptr) + static XrResult CheckXrResult(XrResult Result, const char* Originator = nullptr, const char* SourceLocation = nullptr) { - if (XR_FAILED(res)) - LogXrError(res, originator, sourceLocation); + if (XR_FAILED(Result)) + LogXrError(Result, Originator, SourceLocation); - return res; + return Result; } #define FE_OPENXR_ERROR(cmd) CheckXrResult(cmd, #cmd, FILE_AND_LINE); @@ -192,5 +192,10 @@ namespace FocalEngine void CreateReferenceSpace(); }; -#define FEOpenXR_CORE FEOpenXRCore::getInstance() +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetOpenXRCore(); + #define FEOpenXR_CORE (*static_cast(GetOpenXRCore())) +#else + #define FEOpenXR_CORE FEOpenXRCore::GetInstance() +#endif } \ No newline at end of file diff --git a/SubSystems/FEOpenXR/FEOpenXRInput.cpp b/SubSystems/FEOpenXR/FEOpenXRInput.cpp index 9efbeb1..58062f4 100644 --- a/SubSystems/FEOpenXR/FEOpenXRInput.cpp +++ b/SubSystems/FEOpenXR/FEOpenXRInput.cpp @@ -2,7 +2,13 @@ using namespace FocalEngine; -FEOpenXRInput* FEOpenXRInput::Instance = nullptr; +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetOpenXRInput() +{ + return FEOpenXRInput::GetInstancePointer(); +} +#endif + FEOpenXRInput::FEOpenXRInput() { FEVRControllerActionBindings ValveIndex; @@ -617,7 +623,7 @@ void FEOpenXRInput::SetRightAButtonReleaseCallBack(std::function UserCal reinterpret_cast(Action)->RightDeactivateUserCallBacks.push_back(UserCallBack); } -// FIX ME! Introduce simpler function to get controller type. +// TODO: Need to implement a simpler function to determine controller type. std::string FEOpenXRInput::CurrentlyActiveInteractionProfile(bool bLeftController) { XrPath TopLevelUserPath; diff --git a/SubSystems/FEOpenXR/FEOpenXRInput.h b/SubSystems/FEOpenXR/FEOpenXRInput.h index 7d1cad1..b8bb246 100644 --- a/SubSystems/FEOpenXR/FEOpenXRInput.h +++ b/SubSystems/FEOpenXR/FEOpenXRInput.h @@ -199,5 +199,10 @@ namespace FocalEngine void RegisterAllControllersInOpenXR(); }; -#define FEOpenXR_INPUT FEOpenXRInput::getInstance() +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetOpenXRInput(); + #define FEOpenXR_INPUT (*static_cast(GetOpenXRInput())) +#else + #define FEOpenXR_INPUT FEOpenXRInput::GetInstance() +#endif } \ No newline at end of file diff --git a/SubSystems/FEOpenXR/FEOpenXRRendering.cpp b/SubSystems/FEOpenXR/FEOpenXRRendering.cpp index cd3eec5..36ce7da 100644 --- a/SubSystems/FEOpenXR/FEOpenXRRendering.cpp +++ b/SubSystems/FEOpenXR/FEOpenXRRendering.cpp @@ -2,7 +2,12 @@ using namespace FocalEngine; -FEOpenXRRendering* FEOpenXRRendering::Instance = nullptr; +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetOpenXRRendering() +{ + return FEOpenXRRendering::GetInstancePointer(); +} +#endif FEOpenXRRendering::FEOpenXRRendering() {} FEOpenXRRendering::~FEOpenXRRendering() {} @@ -102,13 +107,13 @@ void FEOpenXRRendering::OpenGLRenderLoop(const XrCompositionLayerProjectionView& { SwapChainFB->Bind(); - const uint32_t colorTexture = reinterpret_cast(SwapChainImage)->image; - FE_GL_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0)); + const uint32_t ColorTexture = reinterpret_cast(SwapChainImage)->image; + FE_GL_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ColorTexture, 0)); - glViewport(static_cast(LayerView.subImage.imageRect.offset.x), - static_cast(LayerView.subImage.imageRect.offset.y), - static_cast(LayerView.subImage.imageRect.extent.width), - static_cast(LayerView.subImage.imageRect.extent.height)); + RENDERER.SetGLViewport(static_cast(LayerView.subImage.imageRect.offset.x), + static_cast(LayerView.subImage.imageRect.offset.y), + static_cast(LayerView.subImage.imageRect.extent.width), + static_cast(LayerView.subImage.imageRect.extent.height)); glEnable(GL_DEPTH_TEST); @@ -124,13 +129,16 @@ void FEOpenXRRendering::OpenGLRenderLoop(const XrCompositionLayerProjectionView& CurrentViewMatrix *= glm::toMat4(EyeOrientation); CurrentViewMatrix = glm::inverse(CurrentViewMatrix); - static FEBasicCamera* CurrentCamera = new FEBasicCamera("VRCamera"); - CurrentCamera->SetPosition(EyePosition); - CurrentCamera->ProjectionMatrix = CurrentProjectionMatrix; - CurrentCamera->ViewMatrix = CurrentViewMatrix; + // FIXME: Need proper camera implementation for VR rendering. + //static FEBasicCamera* CurrentCamera = new FEBasicCamera("VRCamera"); + //CurrentCamera->SetPosition(EyePosition); + //CurrentCamera->ProjectionMatrix = CurrentProjectionMatrix; + //CurrentCamera->ViewMatrix = CurrentViewMatrix; bValidSwapChain = true; - RENDERER.RenderVR(CurrentCamera); + // FIXME: Temporary solution, only supports one scene. + FEScene* CurrentScene = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active)[0]; + RENDERER.RenderVR(CurrentScene); bValidSwapChain = false; } @@ -154,7 +162,7 @@ bool FEOpenXRRendering::RenderLayer(XrTime PredictedDisplayTime, std::vector(GetOpenXRRendering())) +#else + #define FEOpenXR_RENDERING FEOpenXRRendering::GetInstance() +#endif } \ No newline at end of file diff --git a/SubSystems/FEVirtualUIContext.cpp b/SubSystems/FEVirtualUIContext.cpp deleted file mode 100644 index b394620..0000000 --- a/SubSystems/FEVirtualUIContext.cpp +++ /dev/null @@ -1,558 +0,0 @@ -#include "FEVirtualUIContext.h" -using namespace FocalEngine; - -FEVirtualUIContext::FEVirtualUIContext(int Width, int Height, FEMesh* SampleMesh, std::string Name) : FEObject(FE_VIRTUAL_UI_CONTEXT, Name) -{ - if (Width == 0 || Height == 0) - { - LOG.Add("Invalid width or height in FEVirtualUIContext::FEVirtualUIContext", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - CanvasMesh = SampleMesh; - if (CanvasMesh == nullptr) - CanvasMesh = RESOURCE_MANAGER.GetMeshByName("FEPlane")[0]; - - OriginalMeshTrianglePositions = CanvasMesh->GetTrianglePositions(); - TransformedMeshTrianglePositions = OriginalMeshTrianglePositions; - MeshTriangleUVs = CanvasMesh->GetTriangleUVs(); - - CanvasMaterial = RESOURCE_MANAGER.CreateMaterial(); - RESOURCE_MANAGER.MakeMaterialStandard(CanvasMaterial); - CanvasMaterial->Shader = RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/); - CanvasGameModel = RESOURCE_MANAGER.CreateGameModel(CanvasMesh, CanvasMaterial); - RESOURCE_MANAGER.MakeGameModelStandard(CanvasGameModel); - CanvasPrefab = RESOURCE_MANAGER.CreatePrefab(CanvasGameModel); - RESOURCE_MANAGER.MakePrefabStandard(CanvasPrefab); - - Framebuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_COLOR_ATTACHMENT, Width, Height, false); - VirtualUI = APPLICATION.AddVirtualUI(Framebuffer->FBO, Width, Height); - VirtualUI->SetName(Name); - - CanvasMaterial->SetAlbedoMap(Framebuffer->GetColorAttachment()); -} - -FEVirtualUIContext::~FEVirtualUIContext() -{ - delete Framebuffer; - APPLICATION.RemoveVirtualUI(VirtualUI); - - UnregisterCallbacksForWindow(); -} - -void FEVirtualUIContext::GetSize(int* Width, int* Height) const -{ - *Width = Framebuffer->GetWidth(); - *Height = Framebuffer->GetHeight(); -} - -void FEVirtualUIContext::SetSize(int NewWidth, int NewHeight) -{ - if (NewWidth <= 0 || NewHeight <= 0) - { - LOG.Add("Invalid width or height in FEVirtualUIContext::SetSize", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - InvokeResize(NewWidth, NewHeight); -} - -int FEVirtualUIContext::GetWidth() const -{ - return Framebuffer->GetWidth(); -} - -int FEVirtualUIContext::GetHeight() const -{ - return Framebuffer->GetHeight(); -} - -std::function FEVirtualUIContext::GetRenderFunction() -{ - return VirtualUI->GetOnRenderFunction(); -} - -void FEVirtualUIContext::SetRenderFunction(std::function UserRenderFunction) -{ - VirtualUI->SetOnRenderFunction(UserRenderFunction); -} - -void FEVirtualUIContext::ClearRenderFunction() -{ - VirtualUI->ClearOnRenderFunction(); -} - -void FEVirtualUIContext::UpdateCanvasTrianglePositions() -{ - for (size_t i = 0; i < OriginalMeshTrianglePositions.size(); i++) - { - TransformedMeshTrianglePositions[i][0] = glm::vec3(CanvasEntity->Transform.GetTransformMatrix() * glm::vec4(OriginalMeshTrianglePositions[i][0], 1.0f)); - TransformedMeshTrianglePositions[i][1] = glm::vec3(CanvasEntity->Transform.GetTransformMatrix() * glm::vec4(OriginalMeshTrianglePositions[i][1], 1.0f)); - TransformedMeshTrianglePositions[i][2] = glm::vec3(CanvasEntity->Transform.GetTransformMatrix() * glm::vec4(OriginalMeshTrianglePositions[i][2], 1.0f)); - } -} - -void FEVirtualUIContext::UpdateInteractionRay(glm::dvec3 RayOrigin, glm::dvec3 RayDirection) -{ - InteractionRayOrigin = RayOrigin; - InteractionRayDirection = RayDirection; -} - -FEVirtualUI* FEVirtualUIContext::GetVirtualUI() const -{ - return VirtualUI; -} - -bool FEVirtualUIContext::IsInputActive() const -{ - return bActiveInput; -} - -void FEVirtualUIContext::SetInputActive(bool NewValue) -{ - bActiveInput = NewValue; -} - -FETexture* FEVirtualUIContext::GetCurrentFrameTexture() const -{ - return Framebuffer->GetColorAttachment(); -} - -void FEVirtualUIContext::AddOnMouseButtonCallback(std::function UserOnMouseButtonCallback) -{ - VirtualUI->AddOnMouseButtonCallback(UserOnMouseButtonCallback); -} - -void FEVirtualUIContext::InvokeMouseButton(int Button, int Action, int Mods) -{ - if (bActiveInput == false) - return; - - VirtualUI->InvokeMouseButton(Button, Action, Mods); -} - -void FEVirtualUIContext::AddOnMouseMoveCallback(std::function UserOnMouseMoveCallback) -{ - VirtualUI->AddOnMouseMoveCallback(UserOnMouseMoveCallback); -} - -void FEVirtualUIContext::InvokeMouseMove(double Xpos, double Ypos) -{ - if (bActiveInput == false) - return; - - VirtualUI->InvokeMouseMove(Xpos, Ypos); -} - -void FEVirtualUIContext::AddOnResizeCallback(std::function UserOnResizeCallback) -{ - VirtualUI->AddOnResizeCallback(UserOnResizeCallback); -} - -void FEVirtualUIContext::InvokeResize(int Width, int Height) -{ - if (Width <= 0 || Height <= 0) - { - LOG.Add("Invalid width or height in FEVirtualUIContext::InvokeResize", "FE_LOG_RENDERING", FE_LOG_ERROR); - return; - } - - CanvasMaterial->RemoveTexture(Framebuffer->GetColorAttachment()); - delete Framebuffer; - Framebuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_COLOR_ATTACHMENT, Width, Height, false); - CanvasMaterial->SetAlbedoMap(Framebuffer->GetColorAttachment()); - - VirtualUI->InvokeResize(Framebuffer->FBO, Width, Height); -} - -void FEVirtualUIContext::AddOnCharCallback(std::function UserOnCharCallback) -{ - VirtualUI->AddOnCharCallback(UserOnCharCallback); -} - -void FEVirtualUIContext::InvokeCharInput(unsigned int codepoint) -{ - if (!bActiveInput) - return; - - VirtualUI->InvokeCharInput(codepoint); -} - -void FEVirtualUIContext::AddOnKeyCallback(std::function UserOnKeyCallback) -{ - VirtualUI->AddOnKeyCallback(UserOnKeyCallback); -} - -void FEVirtualUIContext::InvokeKeyInput(const int Key, const int Scancode, const int Action, const int Mods) -{ - if (!bActiveInput) - return; - - VirtualUI->InvokeKeyInput(Key, Scancode, Action, Mods); -} - -void FEVirtualUIContext::AddOnScrollCallback(std::function UserOnScrollCallback) -{ - VirtualUI->AddOnScrollCallback(UserOnScrollCallback); -} - -void FEVirtualUIContext::InvokeScrollInput(const double Xoffset, const double Yoffset) -{ - if (!bActiveInput) - return; - - VirtualUI->InvokeScrollInput(Xoffset, Yoffset); -} - -void FEVirtualUIContext::AddOnDropCallback(std::function UserOnDropCallback) -{ - VirtualUI->AddOnDropCallback(UserOnDropCallback); -} - -void FEVirtualUIContext::InvokeDropInput(const int Count, const char** Paths) -{ - if (!bActiveInput) - return; - - VirtualUI->InvokeDropInput(Count, Paths); -} - -void FEVirtualUIContext::MouseButtonListener(int Button, int Action, int Mods) -{ - if (!bActiveInput || !bRayColidingWithCanvas || !bMouseButtonPassThrough) - return; - - if (Button == GLFW_MOUSE_BUTTON_LEFT && Action == GLFW_PRESS) - { - InvokeMouseButton(ImGuiMouseButton_Left, GLFW_PRESS); - } - else if (Button == GLFW_MOUSE_BUTTON_LEFT && Action == GLFW_RELEASE) - { - InvokeMouseButton(ImGuiMouseButton_Left, GLFW_RELEASE); - } - else if (Button == GLFW_MOUSE_BUTTON_RIGHT && Action == GLFW_PRESS) - { - InvokeMouseButton(ImGuiMouseButton_Right, GLFW_PRESS); - } - else if (Button == GLFW_MOUSE_BUTTON_RIGHT && Action == GLFW_RELEASE) - { - InvokeMouseButton(ImGuiMouseButton_Right, GLFW_RELEASE); - } - else if (Button == GLFW_MOUSE_BUTTON_MIDDLE && Action == GLFW_PRESS) - { - InvokeMouseButton(ImGuiMouseButton_Middle, GLFW_PRESS); - } - else if (Button == GLFW_MOUSE_BUTTON_MIDDLE && Action == GLFW_RELEASE) - { - InvokeMouseButton(ImGuiMouseButton_Middle, GLFW_RELEASE); - } -} - -bool FEVirtualUIContext::IsMouseButtonPassThroughActive() const -{ - return bMouseButtonPassThrough; -} - -bool FEVirtualUIContext::SetMouseButtonPassThrough(bool NewValue) -{ - if (WindowToListen == nullptr) - { - LOG.Add("No window to listen in FEVirtualUIContext::SetMouseButtonPassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); - return false; - } - - bMouseButtonPassThrough = NewValue; - return true; -} - -void FEVirtualUIContext::AddOnMouseEnterCallback(std::function UserOnMouseEnterCallback) -{ - VirtualUI->AddOnMouseEnterCallback(UserOnMouseEnterCallback); -} - -void FEVirtualUIContext::InvokeMouseEnterCallback(int Entered) -{ - if (!bActiveInput) - return; - - VirtualUI->InvokeMouseEnterCallback(Entered); -} - -bool FEVirtualUIContext::InteractionRayToCanvasSpace(glm::dvec3 RayOrigin, glm::dvec3 RayDirection, glm::vec2* IntersectionPointInUVCanvasSpace, glm::vec3* IntersectionPointIn3DSpace) -{ - UpdateCanvasTrianglePositions(); - - for (size_t i = 0; i < TransformedMeshTrianglePositions.size(); i++) - { - std::vector CurrentTriangle; - CurrentTriangle.push_back(glm::dvec3(TransformedMeshTrianglePositions[i][0].x, TransformedMeshTrianglePositions[i][0].y, TransformedMeshTrianglePositions[i][0].z)); - CurrentTriangle.push_back(glm::dvec3(TransformedMeshTrianglePositions[i][1].x, TransformedMeshTrianglePositions[i][1].y, TransformedMeshTrianglePositions[i][1].z)); - CurrentTriangle.push_back(glm::dvec3(TransformedMeshTrianglePositions[i][2].x, TransformedMeshTrianglePositions[i][2].y, TransformedMeshTrianglePositions[i][2].z)); - double Distance = 0.0; - glm::dvec3 HitPoint = glm::dvec3(0.0); - double U = 0.0; - double V = 0.0; - - if (GEOMETRY.IsRayIntersectingTriangle(RayOrigin, RayDirection, CurrentTriangle, Distance, &HitPoint, &U, &V)) - { - if (IntersectionPointIn3DSpace != nullptr) - *IntersectionPointIn3DSpace = HitPoint; - - // Load texture coordinates of the triangle vertices. - glm::dvec2 uv0 = MeshTriangleUVs[i][0]; - glm::dvec2 uv1 = MeshTriangleUVs[i][1]; - glm::dvec2 uv2 = MeshTriangleUVs[i][2]; - - // Calculate texture coordinates of the hit point using interpolation. - glm::dvec2 HitUV = (1.0 - U - V) * uv0 + U * uv1 + V * uv2; - *IntersectionPointInUVCanvasSpace = HitUV; - - InvokeMouseEnterCallback(1); - bRayColidingWithCanvas = true; - return true; - } - } - - InvokeMouseEnterCallback(0); - bRayColidingWithCanvas = false; - return false; -} - -void FEVirtualUIContext::MouseMoveListener(double Xpos, double Ypos) -{ - // Not useing bRayColidingWithCanvas here because we want to call UpdateRayIntersection() to determine bRayColidingWithCanvas value. - if (!bActiveInput || !bMouseMovePassThrough) - return; - - UpdateRayIntersection(); -} - -void FEVirtualUIContext::UpdateRayIntersection() -{ - glm::vec2 HitUV = glm::vec2(0.0f); - if (InteractionRayToCanvasSpace(InteractionRayOrigin, InteractionRayDirection, &HitUV)) - InvokeMouseMove(HitUV[0] * GetWidth(), (1.0 - HitUV[1]) * GetHeight()); -} - -bool FEVirtualUIContext::IsMouseMovePassThroughActive() const -{ - return bMouseMovePassThrough; -} - -bool FEVirtualUIContext::SetMouseMovePassThrough(bool NewValue) -{ - if (WindowToListen == nullptr) - { - LOG.Add("No window to listen in FEVirtualUIContext::SetMouseMovePassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); - return false; - } - - bMouseMovePassThrough = NewValue; - return true; -} - -void FEVirtualUIContext::CharListener(unsigned int Codepoint) -{ - if (!bActiveInput || !bRayColidingWithCanvas || !bCharPassThrough) - return; - - InvokeCharInput(Codepoint); -} - -bool FEVirtualUIContext::IsCharPassThroughActive() const -{ - return bCharPassThrough; -} - -bool FEVirtualUIContext::SetCharPassThrough(bool NewValue) -{ - if (WindowToListen == nullptr) - { - LOG.Add("No window to listen in FEVirtualUIContext::SetCharPassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); - return false; - } - - bCharPassThrough = NewValue; - return true; -} - -void FEVirtualUIContext::KeyListener(int Key, int Scancode, int Action, int Mods) -{ - if (!bActiveInput || !bRayColidingWithCanvas || !bKeyPassThrough) - return; - - InvokeKeyInput(Key, Scancode, Action, Mods); -} - -bool FEVirtualUIContext::IsKeyPassThroughActive() const -{ - return bKeyPassThrough; -} - -bool FEVirtualUIContext::SetKeyPassThrough(bool NewValue) -{ - if (WindowToListen == nullptr) - { - LOG.Add("No window to listen in FEVirtualUIContext::SetKeyPassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); - return false; - } - - bKeyPassThrough = NewValue; - return true; -} - -void FEVirtualUIContext::DropListener(int Count, const char** Paths) -{ - if (!bActiveInput || !bRayColidingWithCanvas || !bDropPassThrough) - return; - - InvokeDropInput(Count, Paths); -} - -bool FEVirtualUIContext::IsDropPassThroughActive() const -{ - return bDropPassThrough; -} - -bool FEVirtualUIContext::SetDropPassThrough(bool NewValue) -{ - if (WindowToListen == nullptr) - { - LOG.Add("No window to listen in FEVirtualUIContext::SetDropPassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); - return false; - } - - bDropPassThrough = NewValue; - return true; -} - -void FEVirtualUIContext::ScrollListener(double Xoffset, double Yoffset) -{ - if (!bActiveInput || !bRayColidingWithCanvas || !bScrollPassThrough) - return; - - InvokeScrollInput(Xoffset, Yoffset); -} - -bool FEVirtualUIContext::IsScrollPassThroughActive() const -{ - return bScrollPassThrough; -} - -bool FEVirtualUIContext::SetScrollPassThrough(bool NewValue) -{ - if (WindowToListen == nullptr) - { - LOG.Add("No window to listen in FEVirtualUIContext::SetScrollPassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); - return false; - } - - bScrollPassThrough = NewValue; - return true; -} - -void FEVirtualUIContext::RegisterCallbacksForWindow() -{ - if (WindowToListen == nullptr) - return; - - auto MouseButtonCallbackToRegister = std::bind(&FEVirtualUIContext::MouseButtonListener, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); - WindowToListen->AddOnMouseButtonCallback(MouseButtonCallbackToRegister); - - auto MouseMoveCallbackToRegister = std::bind(&FEVirtualUIContext::MouseMoveListener, this, std::placeholders::_1, std::placeholders::_2); - WindowToListen->AddOnMouseMoveCallback(MouseMoveCallbackToRegister); - - auto CharCallbackToRegister = std::bind(&FEVirtualUIContext::CharListener, this, std::placeholders::_1); - WindowToListen->AddOnCharCallback(CharCallbackToRegister); - - auto KeyCallbackToRegister = std::bind(&FEVirtualUIContext::KeyListener, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - WindowToListen->AddOnKeyCallback(KeyCallbackToRegister); - - auto DropCallbackToRegister = std::bind(&FEVirtualUIContext::DropListener, this, std::placeholders::_1, std::placeholders::_2); - WindowToListen->AddOnDropCallback(DropCallbackToRegister); - - auto ScrollCallbackToRegister = std::bind(&FEVirtualUIContext::ScrollListener, this, std::placeholders::_1, std::placeholders::_2); - WindowToListen->AddOnScrollCallback(ScrollCallbackToRegister); -} - -void FEVirtualUIContext::UnregisterCallbacksForWindow() -{ - if (WindowToListen == nullptr) - return; - - auto MouseButtonCallbackToUnregister = std::bind(&FEVirtualUIContext::MouseButtonListener, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); - WindowToListen->RemoveOnMouseButtonCallback(MouseButtonCallbackToUnregister); - - auto MouseMoveCallbackToUnregister = std::bind(&FEVirtualUIContext::MouseMoveListener, this, std::placeholders::_1, std::placeholders::_2); - WindowToListen->RemoveOnMouseMoveCallback(MouseMoveCallbackToUnregister); - - auto CharCallbackToUnregister = std::bind(&FEVirtualUIContext::CharListener, this, std::placeholders::_1); - WindowToListen->RemoveOnCharCallback(CharCallbackToUnregister); - - auto KeyCallbackToUnregister = std::bind(&FEVirtualUIContext::KeyListener, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - WindowToListen->RemoveOnKeyCallback(KeyCallbackToUnregister); - - auto DropCallbackToUnregister = std::bind(&FEVirtualUIContext::DropListener, this, std::placeholders::_1, std::placeholders::_2); - WindowToListen->RemoveOnDropCallback(DropCallbackToUnregister); - - auto ScrollCallbackToUnregister = std::bind(&FEVirtualUIContext::ScrollListener, this, std::placeholders::_1, std::placeholders::_2); - WindowToListen->RemoveOnScrollCallback(ScrollCallbackToUnregister); -} - -void FEVirtualUIContext::SetWindowToListen(FEWindow* Window) -{ - if (WindowToListen != nullptr && WindowToListen != Window) - UnregisterCallbacksForWindow(); - - WindowToListen = Window; - RegisterCallbacksForWindow(); -} -FEWindow* FEVirtualUIContext::GetWindowToListen() const -{ - return WindowToListen; -} - -glm::vec3 FEVirtualUIContext::GetPosition() const -{ - return CanvasEntity->Transform.GetPosition(); -} - -void FEVirtualUIContext::SetPosition(glm::vec3 NewPosition) -{ - CanvasEntity->Transform.SetPosition(NewPosition); -} - -glm::vec3 FEVirtualUIContext::GetRotation() const -{ - return CanvasEntity->Transform.GetRotation(); -} - -void FEVirtualUIContext::SetRotation(glm::vec3 NewRotation) -{ - CanvasEntity->Transform.SetRotation(NewRotation); -} - -glm::vec3 FEVirtualUIContext::GetScale() const -{ - return CanvasEntity->Transform.GetScale(); -} - -void FEVirtualUIContext::SetScale(glm::vec3 NewScale) -{ - CanvasEntity->Transform.SetScale(NewScale); -} - -bool FEVirtualUIContext::IsVisible() const -{ - return CanvasEntity->IsVisible(); -} - -void FEVirtualUIContext::SetVisibility(bool NewValue) -{ - CanvasEntity->SetVisibility(NewValue); -} - -void FEVirtualUIContext::ExecuteFunctionToAddFont(std::function Func, std::function CallbackOnFontReady) -{ - VirtualUI->ExecuteFunctionToAddFont(Func, CallbackOnFontReady); -} \ No newline at end of file diff --git a/SubSystems/FileSystem/FEAssetPackage.cpp b/SubSystems/FileSystem/FEAssetPackage.cpp new file mode 100644 index 0000000..4780da8 --- /dev/null +++ b/SubSystems/FileSystem/FEAssetPackage.cpp @@ -0,0 +1,762 @@ +#include "FEAssetPackage.h" +#include "../ResourceManager/FEResourceManager.h" +#include "../SubSystems/Scene/FESceneManager.h" +using namespace FocalEngine; + +std::string FEAssetPackage::HeaderStartPhrase = "FEAssetPackage"; +FEAssetPackage::FEAssetPackage() : FEObject(FE_OBJECT_TYPE::FE_ASSET_PACKAGE, "Unnamed asset package") {} + +FEAssetPackage::FEAssetPackage(std::string PackageName, std::vector FilesToAdd) : FEObject(FE_OBJECT_TYPE::FE_ASSET_PACKAGE, PackageName) +{ + for (size_t i = 0; i < FilesToAdd.size(); i++) + { + if (ImportAssetFromFile(FilesToAdd[i]).empty()) + { + LOG.Add("FEAssetPackage::FEAssetPackage: Could not add file to the package: " + FilesToAdd[i], "FE_ASSET_PACKAGE", FE_LOG_ERROR); + } + } +} + +bool FEAssetPackage::IsAssetIDPresent(const std::string& ID) +{ + return Header.Entries.find(ID) != Header.Entries.end(); +} + +std::string FEAssetPackage::ImportAssetFromFile(const std::string& FilePath, FEAssetPackageEntryIntializeData IntializeData) +{ + // For time being, we will not support hierarchical asset packages. + // Because of this, we will not support adding directories to the asset package. + if (FILE_SYSTEM.DoesDirectoryExist(FilePath)) + { + LOG.Add("FEAssetPackage::AddFile: Directories are not supported in asset packages: " + FilePath, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return ""; + } + + // First check if file exists. + if (!FILE_SYSTEM.DoesFileExist(FilePath)) + { + LOG.Add("FEAssetPackage::AddFile: File does not exist: " + FilePath, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return ""; + } + + std::string IDToUse = IntializeData.IsEmpty() ? APPLICATION.GetUniqueHexID() : IntializeData.ID.empty() ? APPLICATION.GetUniqueHexID() : IntializeData.ID; + if (IsAssetIDPresent(IDToUse)) + { + LOG.Add("FEAssetPackage::AddFile: Asset ID already present: " + IDToUse, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return ""; + } + + std::string NameToUse = IntializeData.IsEmpty() ? FILE_SYSTEM.GetFileName(FilePath) : IntializeData.Name.empty() ? FILE_SYSTEM.GetFileName(FilePath) : IntializeData.Name; + std::string TypeToUse = IntializeData.IsEmpty() ? "" : IntializeData.Type; + std::string TagToUse = IntializeData.IsEmpty() ? "" : IntializeData.Tag; + std::string CommentToUse = IntializeData.IsEmpty() ? "" : IntializeData.Comment; + + // Now we will try to read raw data from the file. + std::ifstream File(FilePath, std::ios::binary); + if (!File.is_open()) + { + LOG.Add("FEAssetPackage::AddFile: Could not open file: " + FilePath, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return ""; + } + + File.seekg(0, std::ios::end); + size_t FileSize = File.tellg(); + File.seekg(0, std::ios::beg); + + std::vector FileData(FileSize); + File.read(FileData.data(), FileSize); + File.close(); + + // Now we will add the file to the asset package data. + FEAssetPackageAssetInfo NewEntry; + NewEntry.ID = IDToUse; + NewEntry.Name = NameToUse; + NewEntry.TimeStamp = FILE_SYSTEM.GetFileLastWriteTime(FilePath); + NewEntry.Size = FileSize; + NewEntry.Offset = Data.size(); + NewEntry.Type = TypeToUse; + NewEntry.Tag = TagToUse; + NewEntry.Comment = CommentToUse; + + Header.Entries[NewEntry.ID] = NewEntry; + + Header.EntriesCount++; + UpdateHeaderSize(); + + // And finally, add the file data to the asset package data. + Data.insert(Data.end(), FileData.begin(), FileData.end()); + + return IDToUse; +} + +std::string FEAssetPackage::ImportAssetFromMemory(unsigned char* RawData, size_t Size, FEAssetPackageEntryIntializeData IntializeData) +{ + std::string IDToUse = IntializeData.IsEmpty() ? APPLICATION.GetUniqueHexID() : IntializeData.ID.empty() ? APPLICATION.GetUniqueHexID() : IntializeData.ID; + if (IsAssetIDPresent(IDToUse)) + { + LOG.Add("FEAssetPackage::AddFile: Asset ID already present: " + IDToUse, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return ""; + } + + std::string NameToUse = IntializeData.IsEmpty() ? "" : IntializeData.Name; + std::string TypeToUse = IntializeData.IsEmpty() ? "" : IntializeData.Type; + std::string TagToUse = IntializeData.IsEmpty() ? "" : IntializeData.Tag; + std::string CommentToUse = IntializeData.IsEmpty() ? "" : IntializeData.Comment; + + // Now we will add object to the asset package data. + FEAssetPackageAssetInfo NewEntry; + NewEntry.ID = IDToUse; + NewEntry.Name = NameToUse; + NewEntry.TimeStamp = TIME.GetTimeStamp(FE_TIME_RESOLUTION_NANOSECONDS); + NewEntry.Size = Size; + NewEntry.Offset = Data.size(); + NewEntry.Type = TypeToUse; + NewEntry.Tag = TagToUse; + NewEntry.Comment = CommentToUse; + + Header.Entries[NewEntry.ID] = NewEntry; + + Header.EntriesCount++; + UpdateHeaderSize(); + + // And finally, add the object data to the asset package data. + Data.insert(Data.end(), RawData, RawData + Size); + + return IDToUse; +} + +std::string FEAssetPackage::ImportAsset(FEObject* Object, FEAssetPackageEntryIntializeData IntializeData) +{ + if (Object == nullptr) + { + LOG.Add("FEAssetPackage::ImportAsset: Object is nullptr.", "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return ""; + } + + std::string IDToUse = IntializeData.IsEmpty() ? APPLICATION.GetUniqueHexID() : IntializeData.ID.empty() ? APPLICATION.GetUniqueHexID() : IntializeData.ID; + if (IsAssetIDPresent(IDToUse)) + { + LOG.Add("FEAssetPackage::ImportAsset: Asset ID already present: " + IDToUse, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return ""; + } + + std::string NameToUse = IntializeData.IsEmpty() ? Object->GetName() : IntializeData.Name.empty() ? Object->GetName() : IntializeData.Name; + std::string TypeToUse = IntializeData.IsEmpty() ? "" : IntializeData.Type; + std::string TagToUse = IntializeData.IsEmpty() ? Object->GetTag() : IntializeData.Tag; + std::string CommentToUse = IntializeData.IsEmpty() ? "" : IntializeData.Comment; + + // TODO: This should save info to memory, not to file. + switch (Object->GetType()) + { + case FE_OBJECT_TYPE::FE_TEXTURE: + { + FETexture* Texture = reinterpret_cast(Object); + + if (TypeToUse.empty()) + TypeToUse = "FE_TEXTURE"; + + std::string FilePath = FILE_SYSTEM.GetCurrentWorkingPath() + "TempTexture.texture"; + RESOURCE_MANAGER.SaveFETexture(Texture, FilePath.c_str()); + std::string ResultingID = ImportAssetFromFile(FilePath, FEAssetPackageEntryIntializeData{ IDToUse, NameToUse, TypeToUse, TagToUse, CommentToUse }); + FILE_SYSTEM.DeleteFile(FilePath); + + return ResultingID; + } + + case FE_OBJECT_TYPE::FE_MESH: + { + FEMesh* Mesh = reinterpret_cast(Object); + + if (TypeToUse.empty()) + TypeToUse = "FE_MESH"; + + std::string FilePath = FILE_SYSTEM.GetCurrentWorkingPath() + "TempMesh.mesh"; + RESOURCE_MANAGER.SaveFEMesh(Mesh, FilePath.c_str()); + std::string ResultingID = ImportAssetFromFile(FilePath, FEAssetPackageEntryIntializeData{ IDToUse, NameToUse, TypeToUse, TagToUse, CommentToUse }); + FILE_SYSTEM.DeleteFile(FilePath); + + return ResultingID; + } + + case FE_OBJECT_TYPE::FE_MATERIAL: + { + FEMaterial* Material = reinterpret_cast(Object); + + if (TypeToUse.empty()) + TypeToUse = "FE_MATERIAL"; + + Json::Value JsonRepresentation = RESOURCE_MANAGER.SaveMaterialToJSON(Material); + + Json::StreamWriterBuilder Builder; + const std::string JsonFile = Json::writeString(Builder, JsonRepresentation); + + std::string FilePath = FILE_SYSTEM.GetCurrentWorkingPath() + "TempMaterial.material"; + std::ofstream ResourcesFile; + ResourcesFile.open(FilePath); + ResourcesFile << JsonFile; + ResourcesFile.close(); + + std::string ResultingID = ImportAssetFromFile(FilePath, FEAssetPackageEntryIntializeData{ IDToUse, NameToUse, TypeToUse, TagToUse, CommentToUse }); + FILE_SYSTEM.DeleteFile(FilePath); + + return ResultingID; + } + + case FE_OBJECT_TYPE::FE_GAMEMODEL: + { + FEGameModel* GameModel = reinterpret_cast(Object); + + if (TypeToUse.empty()) + TypeToUse = "FE_GAMEMODEL"; + + Json::Value JsonRepresentation = RESOURCE_MANAGER.SaveGameModelToJSON(GameModel); + + Json::StreamWriterBuilder Builder; + const std::string JsonFile = Json::writeString(Builder, JsonRepresentation); + + std::string FilePath = FILE_SYSTEM.GetCurrentWorkingPath() + "TempGameModel.gamemodel"; + std::ofstream ResourcesFile; + ResourcesFile.open(FilePath); + ResourcesFile << JsonFile; + ResourcesFile.close(); + + std::string ResultingID = ImportAssetFromFile(FilePath, FEAssetPackageEntryIntializeData{ IDToUse, NameToUse, TypeToUse, TagToUse, CommentToUse }); + FILE_SYSTEM.DeleteFile(FilePath); + + return ResultingID; + } + + case FE_OBJECT_TYPE::FE_PREFAB: + { + FEPrefab* Prefab = reinterpret_cast(Object); + + if (TypeToUse.empty()) + TypeToUse = "FE_PREFAB"; + + Json::Value JsonRepresentation = RESOURCE_MANAGER.SavePrefabToJSON(Prefab); + + Json::StreamWriterBuilder Builder; + const std::string JsonFile = Json::writeString(Builder, JsonRepresentation); + + std::string FilePath = FILE_SYSTEM.GetCurrentWorkingPath() + "TempPrefab.prefab"; + std::ofstream ResourcesFile; + ResourcesFile.open(FilePath); + ResourcesFile << JsonFile; + ResourcesFile.close(); + + std::string ResultingID = ImportAssetFromFile(FilePath, FEAssetPackageEntryIntializeData{ IDToUse, NameToUse, TypeToUse, TagToUse, CommentToUse }); + FILE_SYSTEM.DeleteFile(FilePath); + + return ResultingID; + } + + case FE_OBJECT_TYPE::FE_NATIVE_SCRIPT_MODULE: + { + FENativeScriptModule* NativeScriptModule = reinterpret_cast(Object); + + if (TypeToUse.empty()) + TypeToUse = "FE_NATIVE_SCRIPT_MODULE"; + + std::string FilePath = FILE_SYSTEM.GetCurrentWorkingPath() + "TempNativeScriptModule.nativescriptmodule"; + RESOURCE_MANAGER.SaveFENativeScriptModule(NativeScriptModule, FilePath); + std::string ResultingID = ImportAssetFromFile(FilePath, FEAssetPackageEntryIntializeData{ IDToUse, NameToUse, TypeToUse, TagToUse, CommentToUse }); + FILE_SYSTEM.DeleteFile(FilePath); + + return ResultingID; + } + + case FE_OBJECT_TYPE::FE_SCENE: + { + FEScene* Scene = reinterpret_cast(Object); + + if (TypeToUse.empty()) + TypeToUse = "FE_SCENE"; + + Json::Value JsonRepresentation = SCENE_MANAGER.SaveSceneToJSON(Scene); + Json::StreamWriterBuilder Builder; + const std::string JsonFile = Json::writeString(Builder, JsonRepresentation); + + std::string FilePath = FILE_SYSTEM.GetCurrentWorkingPath() + "TempScene.scene"; + std::ofstream ResourcesFile; + ResourcesFile.open(FilePath); + ResourcesFile << JsonFile; + ResourcesFile.close(); + + std::string ResultingID = ImportAssetFromFile(FilePath, FEAssetPackageEntryIntializeData{ IDToUse, NameToUse, TypeToUse, TagToUse, CommentToUse }); + FILE_SYSTEM.DeleteFile(FilePath); + + return ResultingID; + } + + default: + { + LOG.Add("FEAssetPackage::ImportAsset: Object type not supported: " + Object->GetName(), "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return ""; + } + } + + return ""; +} + +bool FEAssetPackage::RemoveAsset(const std::string& ID) +{ + if (!IsAssetIDPresent(ID)) + { + LOG.Add("FEAssetPackage::RemoveFile: Asset ID not present: " + ID, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + FEAssetPackageAssetInfo EntryToDelete = Header.Entries[ID]; + Data.erase(Data.begin() + EntryToDelete.Offset, Data.begin() + EntryToDelete.Offset + EntryToDelete.Size); + + Header.Entries.erase(ID); + Header.EntriesCount--; + UpdateHeaderSize(); + + // Now we need to update the offsets of the assets that are after the removed asset. + for (auto& Entry : Header.Entries) + { + if (Entry.second.Offset > EntryToDelete.Offset) + Entry.second.Offset -= EntryToDelete.Size; + } + + return true; +} + +bool FEAssetPackage::UpdateAssetFromFile(const std::string& ID, const std::string& FilePath) +{ + if (!IsAssetIDPresent(ID)) + { + LOG.Add("FEAssetPackage::UpdateFile: Asset ID not present: " + ID, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + if (!FILE_SYSTEM.DoesFileExist(FilePath)) + { + LOG.Add("FEAssetPackage::UpdateFile: File does not exist: " + FilePath, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + FEAssetPackageAssetInfo OldEntryInfo = Header.Entries[ID]; + if (!RemoveAsset(ID)) + { + LOG.Add("FEAssetPackage::UpdateFile: Could not remove old asset: " + ID, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + std::string NewID = ImportAssetFromFile(FilePath, FEAssetPackageEntryIntializeData{ OldEntryInfo.ID, OldEntryInfo.Name, OldEntryInfo.Type, OldEntryInfo.Tag, OldEntryInfo.Comment }); + if (NewID.empty()) + { + LOG.Add("FEAssetPackage::UpdateFile: Could not import file: " + FilePath, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + if (NewID != ID) + { + LOG.Add("FEAssetPackage::UpdateFile: New ID is different from the old ID.", "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + return true; +} + +bool FEAssetPackage::UpdateAssetFromMemory(const std::string& ID, unsigned char* RawData, size_t Size) +{ + if (!IsAssetIDPresent(ID)) + { + LOG.Add("FEAssetPackage::UpdateFile: Asset ID not present: " + ID, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + FEAssetPackageAssetInfo OldEntryInfo = Header.Entries[ID]; + if (!RemoveAsset(ID)) + { + LOG.Add("FEAssetPackage::UpdateFile: Could not remove old asset: " + ID, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + std::string NewID = ImportAssetFromMemory(RawData, Size, FEAssetPackageEntryIntializeData{ OldEntryInfo.ID, OldEntryInfo.Name, OldEntryInfo.Type, OldEntryInfo.Tag, OldEntryInfo.Comment }); + if (NewID.empty()) + { + LOG.Add("FEAssetPackage::UpdateFile: Could not import file from memory.", "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + if (NewID != ID) + { + LOG.Add("FEAssetPackage::UpdateFile: New ID is different from the old ID.", "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + return true; +} + +void FEAssetPackage::UpdateHeaderSize() +{ + // Update header time stamp. + Header.BuildTimeStamp = TIME.GetTimeStamp(FE_TIME_RESOLUTION_NANOSECONDS); + + Header.Size = HeaderStartPhrase.size(); + // Size of variable that holds the header size. + Header.Size += sizeof(size_t); + // Size of variable that holds the format version. + Header.Size += sizeof(size_t); + // Size of variable that holds the build time stamp. + Header.Size += sizeof(size_t); + // Size of variable that holds the number of assets. + Header.Size += sizeof(size_t); + + // Now will go through all the asset package entries and add their sizes. + for (auto& Entry : Header.Entries) + { + Header.Size += sizeof(size_t); + Header.Size += Entry.second.ID.size(); + Header.Size += sizeof(size_t); + Header.Size += Entry.second.Name.size(); + Header.Size += sizeof(size_t); + Header.Size += Entry.second.Type.size(); + Header.Size += sizeof(size_t); + Header.Size += Entry.second.Tag.size(); + Header.Size += sizeof(size_t); + Header.Size += Entry.second.Comment.size(); + + // Size of variable that holds the size, offset of asset and time stamp. + Header.Size += sizeof(size_t) * 3; + } +} + +FEAssetPackage::~FEAssetPackage() {} + +bool FEAssetPackage::SaveToFile(const std::string& FilePath) +{ + std::ofstream File(FilePath, std::ios::binary); + if (!File.is_open()) + { + LOG.Add("FEAssetPackage::WriteToFile: Could not open file: " + FilePath, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + // Here we will use ExportAsRawData to get the raw data of the asset package. + size_t DataSize = 0; + unsigned char* RawData = ExportAsRawData(DataSize); + if (RawData == nullptr) + { + LOG.Add("FEAssetPackage::WriteToFile: Could not export asset package as raw data.", "FE_ASSET_PACKAGE", FE_LOG_ERROR); + File.close(); + FILE_SYSTEM.DeleteFile(FilePath); + return false; + } + + if (DataSize == 0) + { + LOG.Add("FEAssetPackage::WriteToFile: Asset package size is 0.", "FE_ASSET_PACKAGE", FE_LOG_ERROR); + File.close(); + delete[] RawData; + FILE_SYSTEM.DeleteFile(FilePath); + return false; + } + + File.write((char*)RawData, DataSize); + delete[] RawData; + + // Sanity check, if file written data is not equal to the header size, log an error. + size_t WrittenDataSize = File.tellp(); + if (WrittenDataSize != Header.Size + Data.size()) + { + LOG.Add("FEAssetPackage::WriteToFile: package size is not equal to the written data size.", "FE_ASSET_PACKAGE", FE_LOG_ERROR); + File.close(); + FILE_SYSTEM.DeleteFile(FilePath); + return false; + } + + return true; +} + +bool FEAssetPackage::LoadFromFile(const std::string& FilePath) +{ + std::ifstream File(FilePath, std::ios::binary); + if (!File.is_open()) + { + LOG.Add("FEAssetPackage::LoadFromFile: Could not open file: " + FilePath, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + // Here we will use LoadFromMemory with the raw data of the file. + File.seekg(0, std::ios::end); + size_t FileSize = File.tellg(); + File.seekg(0, std::ios::beg); + + unsigned char* RawData = new unsigned char[FileSize]; + File.read((char*)RawData, FileSize); + File.close(); + + if (!LoadFromMemory(RawData, FileSize)) + { + LOG.Add("FEAssetPackage::LoadFromFile: Could not properly load asset package from file: " + FilePath, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + delete[] RawData; + return false; + } + + return true; +} + +FEAssetPackageAssetInfo FEAssetPackage::GetAssetInfo(const std::string& ID) +{ + if (!IsAssetIDPresent(ID)) + { + LOG.Add("FEAssetPackage::GetAssetInfo: Asset ID not present: " + ID, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return FEAssetPackageAssetInfo(); + } + + return Header.Entries[ID]; +} + +std::vector FEAssetPackage::GetEntryList() +{ + std::vector Result; + for (auto& Entry : Header.Entries) + { + Result.push_back(Entry.second); + } + + return Result; +} + +char* FEAssetPackage::GetAssetDataCopy(const std::string& ID) +{ + if (!IsAssetIDPresent(ID)) + { + LOG.Add("FEAssetPackage::GetAssetDataCopy: Asset ID not present: " + ID, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return nullptr; + } + + FEAssetPackageAssetInfo& Entry = Header.Entries[ID]; + char* DataToReturn = new char[Entry.Size]; + memcpy(DataToReturn, Data.data() + Entry.Offset, Entry.Size); + + return DataToReturn; +} + +bool FEAssetPackage::ExportAssetToFile(const std::string& ID, const std::string& FilePath) +{ + if (!IsAssetIDPresent(ID)) + { + LOG.Add("FEAssetPackage::ExportAssetToFile: Asset ID not present: " + ID, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + FEAssetPackageAssetInfo& Entry = Header.Entries[ID]; + std::ofstream File(FilePath, std::ios::binary); + if (!File.is_open()) + { + LOG.Add("FEAssetPackage::ExportAssetToFile: Could not open file: " + FilePath, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + File.write(Data.data() + Entry.Offset, Entry.Size); + return true; +} + +bool FEAssetPackage::ExportAssetToMemory(const std::string& ID, unsigned char*& RawData, size_t& Size) +{ + if (!IsAssetIDPresent(ID)) + { + LOG.Add("FEAssetPackage::ExportAssetToMemory: Asset ID not present: " + ID, "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + + FEAssetPackageAssetInfo& Entry = Header.Entries[ID]; + RawData = new unsigned char[Entry.Size]; + memcpy(RawData, Data.data() + Entry.Offset, Entry.Size); + Size = Entry.Size; + + return true; +} + +size_t FEAssetPackage::GetBuildTimeStamp() +{ + return Header.BuildTimeStamp; +} + +std::string FEAssetPackage::GetBuildTimeStampAsString() +{ + return TIME.NanosecondTimeStampToDate(Header.BuildTimeStamp); +} + +unsigned char* FEAssetPackage::ExportAsRawData(size_t& Size) +{ + Size = Header.Size + Data.size(); + if (Size == 0) + { + LOG.Add("FEAssetPackage::ExportAsRawData: Asset package size is 0.", "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return nullptr; + } + + // First we will write the header. + unsigned char* DataToReturn = new unsigned char[Size]; + unsigned char* CurrentDataPointer = DataToReturn; + // Write the header start phrase. + memcpy(CurrentDataPointer, HeaderStartPhrase.c_str(), HeaderStartPhrase.size()); + CurrentDataPointer += HeaderStartPhrase.size(); + // Write the header size. + memcpy(CurrentDataPointer, &Header.Size, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + // Write the format version. + memcpy(CurrentDataPointer, &Header.FormatVersion, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + // Write the build time stamp. + memcpy(CurrentDataPointer, &Header.BuildTimeStamp, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + // Write the number of entries. + memcpy(CurrentDataPointer, &Header.EntriesCount, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + + for (auto& Entry : Header.Entries) + { + // Write the ID. + size_t IDSize = Entry.second.ID.size(); + memcpy(CurrentDataPointer, &IDSize, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + memcpy(CurrentDataPointer, Entry.second.ID.c_str(), IDSize); + CurrentDataPointer += IDSize; + + // Write the name. + size_t NameSize = Entry.second.Name.size(); + memcpy(CurrentDataPointer, &NameSize, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + memcpy(CurrentDataPointer, Entry.second.Name.c_str(), NameSize); + CurrentDataPointer += NameSize; + + // Write the type. + size_t TypeSize = Entry.second.Type.size(); + memcpy(CurrentDataPointer, &TypeSize, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + memcpy(CurrentDataPointer, Entry.second.Type.c_str(), TypeSize); + CurrentDataPointer += TypeSize; + + // Write the tag. + size_t TagSize = Entry.second.Tag.size(); + memcpy(CurrentDataPointer, &TagSize, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + memcpy(CurrentDataPointer, Entry.second.Tag.c_str(), TagSize); + CurrentDataPointer += TagSize; + + // Write the comment. + size_t CommentSize = Entry.second.Comment.size(); + memcpy(CurrentDataPointer, &CommentSize, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + memcpy(CurrentDataPointer, Entry.second.Comment.c_str(), CommentSize); + CurrentDataPointer += CommentSize; + + // Write the time stamp. + memcpy(CurrentDataPointer, &Entry.second.TimeStamp, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + // Write the size. + memcpy(CurrentDataPointer, &Entry.second.Size, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + // Write the offset. + memcpy(CurrentDataPointer, &Entry.second.Offset, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + } + + // Now we will write the asset package data. + memcpy(CurrentDataPointer, Data.data(), Data.size()); + + return DataToReturn; +} + +bool FEAssetPackage::LoadFromMemory(unsigned char* RawData, size_t Size) +{ + // First we will read the header. + unsigned char* CurrentDataPointer = RawData; + size_t HeaderStartPhraseSize = HeaderStartPhrase.size(); + if (memcmp(CurrentDataPointer, HeaderStartPhrase.c_str(), HeaderStartPhraseSize) != 0) + { + LOG.Add("FEAssetPackage::LoadFromMemory: Header start phrase is not correct.", "FE_ASSET_PACKAGE", FE_LOG_ERROR); + return false; + } + CurrentDataPointer += HeaderStartPhraseSize; + + // Read the header size. + memcpy(&Header.Size, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + // Read the format version. + memcpy(&Header.FormatVersion, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + // Read the build time stamp. + memcpy(&Header.BuildTimeStamp, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + // Read the number of entries. + memcpy(&Header.EntriesCount, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + + for (size_t i = 0; i < Header.EntriesCount; i++) + { + FEAssetPackageAssetInfo NewEntry; + + // Read the ID. + size_t IDSize = 0; + memcpy(&IDSize, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + NewEntry.ID = std::string((char*)CurrentDataPointer, IDSize); + CurrentDataPointer += IDSize; + + // Read the name. + size_t NameSize = 0; + memcpy(&NameSize, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + NewEntry.Name = std::string((char*)CurrentDataPointer, NameSize); + CurrentDataPointer += NameSize; + + // Read the type. + size_t TypeSize = 0; + memcpy(&TypeSize, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + NewEntry.Type = std::string((char*)CurrentDataPointer, TypeSize); + CurrentDataPointer += TypeSize; + + // Read the tag. + size_t TagSize = 0; + memcpy(&TagSize, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + NewEntry.Tag = std::string((char*)CurrentDataPointer, TagSize); + CurrentDataPointer += TagSize; + + // Read the comment. + size_t CommentSize = 0; + memcpy(&CommentSize, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + NewEntry.Comment = std::string((char*)CurrentDataPointer, CommentSize); + CurrentDataPointer += CommentSize; + + // Read the time stamp. + memcpy(&NewEntry.TimeStamp, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + // Read the size. + memcpy(&NewEntry.Size, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + // Read the offset. + memcpy(&NewEntry.Offset, CurrentDataPointer, sizeof(size_t)); + CurrentDataPointer += sizeof(size_t); + + Header.Entries[NewEntry.ID] = NewEntry; + } + + // Now we will read the asset package data. + Data.resize(Size - Header.Size); + memcpy(Data.data(), CurrentDataPointer, Size - Header.Size); + + return true; +} + +std::vector FEAssetPackage::GetAssetIDsByName(const std::string& Name) +{ + std::vector Result; + for (auto& Entry : Header.Entries) + { + if (Entry.second.Name == Name) + Result.push_back(Entry.first); + } + + return Result; +} \ No newline at end of file diff --git a/SubSystems/FileSystem/FEAssetPackage.h b/SubSystems/FileSystem/FEAssetPackage.h new file mode 100644 index 0000000..2c1e279 --- /dev/null +++ b/SubSystems/FileSystem/FEAssetPackage.h @@ -0,0 +1,94 @@ +#pragma once + +#include "FEFileSystem.h" +#include "../Core/FEObject.h" + +namespace FocalEngine +{ + struct FEAssetPackageAssetInfo + { + std::string ID; + std::string Name; + std::string Type; + std::string Tag; + std::string Comment; + + size_t TimeStamp = 0; + size_t Size = 0; + // Offset from the end of the header. + size_t Offset = 0; + }; + + struct FEAssetPackageEntryIntializeData + { + std::string ID; + std::string Name; + std::string Type; + std::string Tag; + std::string Comment; + + bool IsEmpty() + { + return ID.empty() && Name.empty() && Type.empty() && Tag.empty() && Comment.empty(); + } + }; + + struct FEAssetPackageHeader + { + size_t Size = 0; + size_t EntriesCount = 0; + size_t CurrentAssetOffset = 0; + size_t FormatVersion = 1; + size_t BuildTimeStamp = 0; + + std::unordered_map Entries; + }; + + class FOCAL_ENGINE_API FEAssetPackage : public FEObject + { + friend class FEResourceManager; + public: + FEAssetPackage(); + FEAssetPackage(std::string PackageName, std::vector FilesToAdd); + ~FEAssetPackage(); + + bool LoadFromFile(const std::string& FilePath); + bool LoadFromMemory(unsigned char* RawData, size_t Size); + + bool SaveToFile(const std::string& FilePath); + unsigned char* ExportAsRawData(size_t& Size); + + std::string ImportAssetFromFile(const std::string& FilePath, FEAssetPackageEntryIntializeData IntializeData = FEAssetPackageEntryIntializeData()); + bool UpdateAssetFromFile(const std::string& ID, const std::string& FilePath); + bool ExportAssetToFile(const std::string& ID, const std::string& FilePath); + + std::string ImportAssetFromMemory(unsigned char* RawData, size_t Size, FEAssetPackageEntryIntializeData IntializeData = FEAssetPackageEntryIntializeData()); + bool UpdateAssetFromMemory(const std::string& ID, unsigned char* RawData, size_t Size); + bool ExportAssetToMemory(const std::string& ID, unsigned char*& RawData, size_t& Size); + + // Returns the ID of the asset. + // Accepts a FEObject* to get the asset data from. + std::string ImportAsset(FEObject* Object, FEAssetPackageEntryIntializeData IntializeData = FEAssetPackageEntryIntializeData()); + + bool IsAssetIDPresent(const std::string& ID); + bool RemoveAsset(const std::string& ID); + + FEAssetPackageAssetInfo GetAssetInfo(const std::string& ID); + std::vector GetEntryList(); + char* GetAssetDataCopy(const std::string& ID); + + size_t GetBuildTimeStamp(); + std::string GetBuildTimeStampAsString(); + + std::vector GetAssetIDsByName(const std::string& Name); + private: + // String that each asset package has to start with. + static std::string HeaderStartPhrase; + + FEAssetPackageHeader Header; + + void UpdateHeaderSize(); + // In memory representation of the asset package. + std::vector Data; + }; +} diff --git a/SubSystems/FEFileSystem.cpp b/SubSystems/FileSystem/FEFileSystem.cpp similarity index 50% rename from SubSystems/FEFileSystem.cpp rename to SubSystems/FileSystem/FEFileSystem.cpp index 284216d..9ded3fb 100644 --- a/SubSystems/FEFileSystem.cpp +++ b/SubSystems/FileSystem/FEFileSystem.cpp @@ -1,10 +1,17 @@ #include "FEFileSystem.h" using namespace FocalEngine; -FEFileSystem* FEFileSystem::Instance = nullptr; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetFileSystem() +{ + return FEFileSystem::GetInstancePointer(); +} +#endif + FEFileSystem::FEFileSystem() {} FEFileSystem::~FEFileSystem() {} -bool FEFileSystem::CheckFile(const std::string& Path) +bool FEFileSystem::DoesFileExist(const std::string& Path) { std::filesystem::path FilePath(Path); return std::filesystem::exists(FilePath) && std::filesystem::is_regular_file(FilePath); @@ -19,7 +26,21 @@ bool FEFileSystem::RenameFile(const std::string& Path, const std::string& NewPat } catch (const std::exception& Exception) { - LOG.Add("Error in FEFileSystem::ChangeFileName: " + std::string(Exception.what()), "FE_LOG_GENERAL", FE_LOG_ERROR); + LOG.Add("Error in FEFileSystem::ChangeFileName: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); + return false; + } +} + +bool FEFileSystem::CopyFile(const std::string& Path, const std::string& NewPath) +{ + try + { + std::filesystem::copy(Path, NewPath); + return true; + } + catch (const std::exception& Exception) + { + LOG.Add("Error in FEFileSystem::CopyFile: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); return false; } } @@ -32,12 +53,94 @@ bool FEFileSystem::DeleteFile(const std::string& Path) } catch (const std::exception& Exception) { - LOG.Add("Error in FEFileSystem::DeleteFile: " + std::string(Exception.what()), "FE_LOG_GENERAL", FE_LOG_ERROR); + LOG.Add("Error in FEFileSystem::DeleteFile: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); return false; } } -bool FEFileSystem::CheckDirectory(const std::string& Path) +size_t FEFileSystem::GetFileSize(const std::string& Path) +{ + std::filesystem::path FilePath(Path); + if (std::filesystem::exists(FilePath) && std::filesystem::is_regular_file(FilePath)) + { + return std::filesystem::file_size(FilePath); + } + + return 0; +} + +uint64_t FEFileSystem::GetFileLastWriteTime(const std::string& Path) +{ + std::filesystem::path FilePath(Path); + if (std::filesystem::exists(FilePath) && std::filesystem::is_regular_file(FilePath)) + { + auto FileTime = std::filesystem::last_write_time(FilePath); + auto TimePoint = std::chrono::time_point_cast(FileTime - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now()); + auto Duration = TimePoint.time_since_epoch(); + auto NanosecondsCount = std::chrono::duration_cast(Duration); + return NanosecondsCount.count(); + } + + return 0; +} + +//void FEFileSystem::WaitForFileAccess(std::string FilePath, int TimeoutInMS) +//{ +// std::filesystem::path Path(FilePath); +// std::chrono::milliseconds Timeout(TimeoutInMS); +// std::chrono::time_point StartTime = std::chrono::system_clock::now(); +// std::filesystem::file_status Status = std::filesystem::status(Path); +// while (std::filesystem::exists(Path) && std::filesystem::is_regular_file(Path)) +// { +// std::this_thread::sleep_for(std::chrono::milliseconds(10)); +// if (std::chrono::duration_cast(std::chrono::system_clock::now() - StartTime) > Timeout) +// break; +// } +//} + +bool FEFileSystem::WaitForFileAccess(const std::string& FilePath, int TimeoutInMS) +{ + const auto start = std::chrono::steady_clock::now(); + const auto end = start + std::chrono::milliseconds(TimeoutInMS); + + while (std::chrono::steady_clock::now() < end) + { + try + { + // Check if file exists + if (!std::filesystem::exists(FilePath)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + continue; + } + + // Try to open the file + std::ifstream file(FilePath); + if (file.is_open()) + { + // File is accessible, close it and return + file.close(); + return true; + } + } + catch (const std::filesystem::filesystem_error&) + { + // Ignore filesystem errors and continue trying + } + catch (const std::ios_base::failure&) + { + // Ignore file open errors and continue trying + } + + // Wait for 10ms before next attempt + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + // If we've reached here, we've timed out + return false; +} + +bool FEFileSystem::DoesDirectoryExist(const std::string& Path) { std::filesystem::path FolderPath(Path); return std::filesystem::exists(FolderPath) && std::filesystem::is_directory(FolderPath); @@ -60,7 +163,7 @@ bool FEFileSystem::RenameDirectory(const std::string& Path, const std::string& N } catch (const std::exception& Exception) { - LOG.Add("Error in FEFileSystem::RenameDirectory: " + std::string(Exception.what()), "FE_LOG_GENERAL", FE_LOG_ERROR); + LOG.Add("Error in FEFileSystem::RenameDirectory: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); return false; } } @@ -73,24 +176,135 @@ bool FEFileSystem::CreateDirectory(const std::string& Path) } catch (const std::exception& Exception) { - LOG.Add("Error in FEFileSystem::CreateDirectory: " + std::string(Exception.what()), "FE_LOG_GENERAL", FE_LOG_ERROR); + LOG.Add("Error in FEFileSystem::CreateDirectory: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); + return false; + } +} + +bool FEFileSystem::CopyDirectory(const std::string& Path, const std::string& NewPath) +{ + try + { + std::filesystem::copy(Path, NewPath, std::filesystem::copy_options::recursive); + } + catch (const std::exception& Exception) + { + LOG.Add("Error in FEFileSystem::CopyDirectory: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); return false; } + + return true; } bool FEFileSystem::DeleteDirectory(const std::string& Path) { try { - return std::filesystem::remove(Path); + return std::filesystem::remove_all(Path) > 0; } catch (const std::exception& Exception) { - LOG.Add("Error in FEFileSystem::DeleteDirectory: " + std::string(Exception.what()), "FE_LOG_GENERAL", FE_LOG_ERROR); + LOG.Add("Error in FEFileSystem::DeleteDirectory: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); return false; } } +std::vector FEFileSystem::GetFileNamesInDirectory(const std::string& Path, bool bRecursive) +{ + std::vector Result; + + try + { + std::filesystem::path Directory(Path); + if (std::filesystem::exists(Directory) && std::filesystem::is_directory(Directory)) + { + for (const auto& Entry : std::filesystem::directory_iterator(Directory)) + { + const auto& Path = Entry.path(); + if (std::filesystem::is_regular_file(Path)) + { + Result.push_back(Path.filename().string()); + } + else if (bRecursive && std::filesystem::is_directory(Path)) + { + std::vector SubDirectoryFiles = GetFileNamesInDirectory(Path.string(), bRecursive); + Result.insert(Result.end(), SubDirectoryFiles.begin(), SubDirectoryFiles.end()); + } + } + } + } + catch (const std::exception& Exception) + { + LOG.Add("Error in FEFileSystem::GetFileNamesInDirectory: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); + } + + return Result; +} + +std::vector FEFileSystem::GetFilesInDirectory(const std::string& Path, bool bRecursive) +{ + std::vector Result; + + try + { + std::filesystem::path Directory(Path); + if (std::filesystem::exists(Directory) && std::filesystem::is_directory(Directory)) + { + for (const auto& Entry : std::filesystem::directory_iterator(Directory)) + { + const auto& Path = Entry.path(); + if (std::filesystem::is_regular_file(Path)) + { + Result.push_back(Path.string()); + } + else if (bRecursive && std::filesystem::is_directory(Path)) + { + std::vector SubDirectoryFiles = GetFilesInDirectory(Path.string(), bRecursive); + Result.insert(Result.end(), SubDirectoryFiles.begin(), SubDirectoryFiles.end()); + } + } + } + } + catch (const std::exception& Exception) + { + LOG.Add("Error in FEFileSystem::GetFilesInDirectory: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); + } + + return Result; +} + +std::vector FEFileSystem::GetFolderChain(const std::string& Path) +{ + std::vector Result; + + try + { + std::filesystem::path Directory(Path); + while (!Directory.string().empty()) + { + //Directory. + //std::filesystem::path test = std::filesystem::canonical(Directory.string() + "/"); + //if (std::filesystem::is_directory(Directory.string() + "/")) + + if (!Result.empty()) + { + if (Result.back() == Directory.string()) + break; + } + Result.push_back(Directory.string()); + Directory = Directory.parent_path(); + } + + std::reverse(Result.begin(), Result.end()); + } + catch (const std::exception& Exception) + { + LOG.Add("Error in FEFileSystem::GetFolderChain: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); + } + + return Result; +} + std::vector FEFileSystem::GetDirectoryList(const std::string& Path) { std::vector Result; @@ -116,7 +330,7 @@ std::vector FEFileSystem::GetDirectoryList(const std::string& Path) } catch (const std::exception& Exception) { - LOG.Add("Error in FEFileSystem::GetFolderList: " + std::string(Exception.what()), "FE_LOG_GENERAL", FE_LOG_ERROR); + LOG.Add("Error in FEFileSystem::GetFolderList: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); } return Result; @@ -143,7 +357,7 @@ std::vector FEFileSystem::GetFileList(const std::string& Path) } catch (const std::exception& Exception) { - LOG.Add("Error in FEFileSystem::GetFileList: " + std::string(Exception.what()), "FE_LOG_GENERAL", FE_LOG_ERROR); + LOG.Add("Error in FEFileSystem::GetFileList: " + std::string(Exception.what()), "FE_FILE_SYSTEM", FE_LOG_ERROR); } return Result; @@ -157,10 +371,10 @@ std::string FEFileSystem::PWSTRtoString(const PWSTR WString) char* SzTo = new char[WFileName.length() + 1]; SzTo[WFileName.size()] = '\0'; WideCharToMultiByte(CP_ACP, 0, WFileName.c_str(), -1, SzTo, static_cast(WFileName.length()), nullptr, nullptr); - std::string result = SzTo; + std::string Result = SzTo; delete[] SzTo; - return result; + return Result; } void FEFileSystem::ShowFileOpenDialog(std::string& Path, const COMDLG_FILTERSPEC* Filter, const int FilterCount) @@ -346,4 +560,55 @@ std::string FEFileSystem::ReadFEString(std::fstream& File) delete[] Buffer; return Result; +} + +bool FEFileSystem::PerformTextReplacements(const std::string& FilePath, const std::vector& Rules) +{ + if (FilePath.empty()) + { + LOG.Add("FEFileSystem::ReplaceInFile: File path is empty", "FE_FILE_SYSTEM", FE_LOG_WARNING); + return false; + } + + std::fstream File(FilePath, std::ios::in); + if (!File.is_open()) + { + LOG.Add("FEFileSystem::ReplaceInFile: Error opening file " + FilePath, "FE_FILE_SYSTEM", FE_LOG_ERROR); + return false; + } + + std::vector FileContent; + std::string Line; + while (std::getline(File, Line)) + FileContent.push_back(Line); + + File.close(); + + // Apply replacements + for (size_t i = 0; i < FileContent.size(); i++) + { + for (size_t j = 0; j < Rules.size(); j++) + { + if (FileContent[i].find(Rules[j].ContextPattern) != std::string::npos) + { + size_t Position = FileContent[i].find(Rules[j].TargetText); + if (Position != std::string::npos) + FileContent[i].replace(Position, Rules[j].TargetText.length(), Rules[j].ReplacementText); + } + } + } + + File.open(FilePath, std::ios::out | std::ios::trunc); + if (!File.is_open()) + { + LOG.Add("FEFileSystem::ReplaceInFile: Error opening file " + FilePath, "FE_FILE_SYSTEM", FE_LOG_ERROR); + return false; + } + + // Write the modified content back to the file + for (size_t i = 0; i < FileContent.size(); i++) + File << FileContent[i] + "\n"; + + File.close(); + return true; } \ No newline at end of file diff --git a/SubSystems/FEFileSystem.h b/SubSystems/FileSystem/FEFileSystem.h similarity index 50% rename from SubSystems/FEFileSystem.h rename to SubSystems/FileSystem/FEFileSystem.h index f2c6e5e..6e294d4 100644 --- a/SubSystems/FEFileSystem.h +++ b/SubSystems/FileSystem/FEFileSystem.h @@ -9,26 +9,48 @@ namespace FocalEngine { - class FEFileSystem + class FOCAL_ENGINE_API FEFileSystem { public: SINGLETON_PUBLIC_PART(FEFileSystem) - bool CheckFile(const std::string& Path); + bool DoesFileExist(const std::string& Path); bool RenameFile(const std::string& Path, const std::string& NewPath); + bool CopyFile(const std::string& Path, const std::string& NewPath); bool DeleteFile(const std::string& Path); std::vector GetFileList(const std::string& Path); + size_t GetFileSize(const std::string& Path); + // Returns last write time in nanoseconds since epoch. + uint64_t GetFileLastWriteTime(const std::string& Path); + bool WaitForFileAccess(const std::string& FilePath, int TimeoutInMS = 1000); std::string GetFileExtension(const std::string& Path); std::string GetDirectoryPath(const std::string& FullPath); std::string GetFileName(const std::string& FullPath); - bool CheckDirectory(const std::string& Path); + bool DoesDirectoryExist(const std::string& Path); bool RenameDirectory(const std::string& Path, const std::string& NewPath); bool CreateDirectory(const std::string& Path); + bool CopyDirectory(const std::string& Path, const std::string& NewPath); bool DeleteDirectory(const std::string& Path); + + std::vector GetFilesInDirectory(const std::string& Path, bool bRecursive = false); + std::vector GetFileNamesInDirectory(const std::string& Path, bool bRecursive = false); std::vector GetDirectoryList(const std::string& Path); + std::vector GetFolderChain(const std::string& Path); + + struct TextReplacementRule + { + // Pattern to identify the line where replacement should occur + std::string ContextPattern; + // Text to be replaced + std::string TargetText; + std::string ReplacementText; + }; + + bool PerformTextReplacements(const std::string& FilePath, const std::vector& Rules); + #ifdef FE_WIN_32 void ShowFileOpenDialog(std::string& Path, const COMDLG_FILTERSPEC* Filter, int FilterCount = 1); void ShowFolderOpenDialog(std::string& Path); @@ -45,5 +67,10 @@ namespace FocalEngine #endif }; - #define FILE_SYSTEM FEFileSystem::getInstance() +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetFileSystem(); + #define FILE_SYSTEM (*static_cast(GetFileSystem())) +#else + #define FILE_SYSTEM FEFileSystem::GetInstance() +#endif } diff --git a/SubSystems/Scene/Components/FECameraComponent.cpp b/SubSystems/Scene/Components/FECameraComponent.cpp new file mode 100644 index 0000000..a99a283 --- /dev/null +++ b/SubSystems/Scene/Components/FECameraComponent.cpp @@ -0,0 +1,601 @@ +#include "FECameraComponent.h" +#include "../ResourceManager/FEResourceManager.h" +#include "Systems/FECameraSystem.h" +#include "../../../FEngine.h" +using namespace FocalEngine; + +FECameraComponent::FECameraComponent() +{ + Frustum.resize(6); + for (size_t i = 0; i < Frustum.size(); i++) + Frustum[i].resize(4); + + Viewport = new FEViewport(); +} + +bool FECameraComponent::IsMainCamera() const +{ + return bIsMainCamera; +} + +glm::mat4 FECameraComponent::GetViewMatrix() const +{ + return ViewMatrix; +} + +void FECameraComponent::SetViewMatrix(const glm::mat4 NewViewMatrix) +{ + ViewMatrix = NewViewMatrix; +} + +glm::mat4 FECameraComponent::GetProjectionMatrix() const +{ + return ProjectionMatrix; +} + +void FECameraComponent::SetProjectionMatrix(const glm::mat4 NewProjectionMatrix) +{ + ProjectionMatrix = NewProjectionMatrix; +} + +glm::vec3 FECameraComponent::GetUp() const +{ + return glm::normalize(glm::vec3(glm::vec4(0.0f, 1.0f, 0.0f, 0.0f) * ViewMatrix)); +} + +glm::vec3 FECameraComponent::GetForward() const +{ + return glm::normalize(glm::vec3(glm::vec4(0.0f, 0.0f, -1.0f, 0.0f) * ViewMatrix)); +} + +glm::vec3 FECameraComponent::GetRight() const +{ + return glm::normalize(glm::vec3(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f) * ViewMatrix)); +} + +bool FECameraComponent::IsActive() const +{ + return bIsActive; +} + +void FECameraComponent::SetActive(const bool Active) +{ + bIsActive = Active; +} + +float FECameraComponent::GetFOV() const +{ + return FOV; +} + +void FECameraComponent::SetFOV(const float FOV) +{ + this->FOV = FOV; +} + +float FECameraComponent::GetNearPlane() const +{ + return NearPlane; +} + +void FECameraComponent::SetNearPlane(const float NearPlane) +{ + this->NearPlane = NearPlane; +} + +float FECameraComponent::GetFarPlane() const +{ + return FarPlane; +} + +void FECameraComponent::SetFarPlane(const float FarPlane) +{ + this->FarPlane = FarPlane; +} + +float FECameraComponent::GetAspectRatio() const +{ + return AspectRatio; +} + +void FECameraComponent::SetAspectRatio(const float AspectRatio) +{ + this->AspectRatio = AspectRatio; +} + +bool FECameraComponent::IsClearColorEnabled() const +{ + return bClearColorEnabled; +} + +void FECameraComponent::SetIsClearColorEnabled(const bool bEnabled) +{ + bClearColorEnabled = bEnabled; +} + +glm::vec4 FECameraComponent::GetClearColor() const +{ + return ClearColor; +} + +void FECameraComponent::SetClearColor(glm::vec4 NewClearColor) +{ + ClearColor = NewClearColor; +} + +float FECameraComponent::GetGamma() const +{ + return Gamma; +} + +void FECameraComponent::SetGamma(const float Gamma) +{ + this->Gamma = Gamma; +} + +float FECameraComponent::GetExposure() const +{ + return Exposure; +} + +void FECameraComponent::SetExposure(const float Exposure) +{ + this->Exposure = Exposure; +} + +void FECameraComponent::UpdateFrustumPlanes() +{ + float Clip[16]; + + glm::mat4 Cliping = GetProjectionMatrix() * GetViewMatrix(); + for (int i = 0; i < 4; i++) + { + Clip[i * 4] = Cliping[i][0]; + Clip[i * 4 + 1] = Cliping[i][1]; + Clip[i * 4 + 2] = Cliping[i][2]; + Clip[i * 4 + 3] = Cliping[i][3]; + } + + /* Extract the numbers for the RIGHT plane */ + Frustum[0][0] = Clip[3] - Clip[0]; + Frustum[0][1] = Clip[7] - Clip[4]; + Frustum[0][2] = Clip[11] - Clip[8]; + Frustum[0][3] = Clip[15] - Clip[12]; + + /* Normalize the result */ + float T = sqrt(Frustum[0][0] * Frustum[0][0] + Frustum[0][1] * Frustum[0][1] + Frustum[0][2] * Frustum[0][2]); + Frustum[0][0] /= T; + Frustum[0][1] /= T; + Frustum[0][2] /= T; + Frustum[0][3] /= T; + + /* Extract the numbers for the LEFT plane */ + Frustum[1][0] = Clip[3] + Clip[0]; + Frustum[1][1] = Clip[7] + Clip[4]; + Frustum[1][2] = Clip[11] + Clip[8]; + Frustum[1][3] = Clip[15] + Clip[12]; + + /* Normalize the result */ + T = sqrt(Frustum[1][0] * Frustum[1][0] + Frustum[1][1] * Frustum[1][1] + Frustum[1][2] * Frustum[1][2]); + Frustum[1][0] /= T; + Frustum[1][1] /= T; + Frustum[1][2] /= T; + Frustum[1][3] /= T; + + /* Extract the BOTTOM plane */ + Frustum[2][0] = Clip[3] + Clip[1]; + Frustum[2][1] = Clip[7] + Clip[5]; + Frustum[2][2] = Clip[11] + Clip[9]; + Frustum[2][3] = Clip[15] + Clip[13]; + + /* Normalize the result */ + T = sqrt(Frustum[2][0] * Frustum[2][0] + Frustum[2][1] * Frustum[2][1] + Frustum[2][2] * Frustum[2][2]); + Frustum[2][0] /= T; + Frustum[2][1] /= T; + Frustum[2][2] /= T; + Frustum[2][3] /= T; + + /* Extract the TOP plane */ + Frustum[3][0] = Clip[3] - Clip[1]; + Frustum[3][1] = Clip[7] - Clip[5]; + Frustum[3][2] = Clip[11] - Clip[9]; + Frustum[3][3] = Clip[15] - Clip[13]; + + /* Normalize the result */ + T = sqrt(Frustum[3][0] * Frustum[3][0] + Frustum[3][1] * Frustum[3][1] + Frustum[3][2] * Frustum[3][2]); + Frustum[3][0] /= T; + Frustum[3][1] /= T; + Frustum[3][2] /= T; + Frustum[3][3] /= T; + + /* Extract the FAR plane */ + Frustum[4][0] = Clip[3] - Clip[2]; + Frustum[4][1] = Clip[7] - Clip[6]; + Frustum[4][2] = Clip[11] - Clip[10]; + Frustum[4][3] = Clip[15] - Clip[14]; + + /* Normalize the result */ + T = sqrt(Frustum[4][0] * Frustum[4][0] + Frustum[4][1] * Frustum[4][1] + Frustum[4][2] * Frustum[4][2]); + Frustum[4][0] /= T; + Frustum[4][1] /= T; + Frustum[4][2] /= T; + Frustum[4][3] /= T; + + /* Extract the NEAR plane */ + Frustum[5][0] = Clip[3] + Clip[2]; + Frustum[5][1] = Clip[7] + Clip[6]; + Frustum[5][2] = Clip[11] + Clip[10]; + Frustum[5][3] = Clip[15] + Clip[14]; + + /* Normalize the result */ + T = sqrt(Frustum[5][0] * Frustum[5][0] + Frustum[5][1] * Frustum[5][1] + Frustum[5][2] * Frustum[5][2]); + Frustum[5][0] /= T; + Frustum[5][1] /= T; + Frustum[5][2] /= T; + Frustum[5][3] /= T; +} + +std::vector> FECameraComponent::GetFrustumPlanes() +{ + return Frustum; +} + +float FECameraComponent::GetRenderScale() +{ + return RenderScale; +} + +int FECameraComponent::GetRenderTargetWidth() const +{ + if (Viewport == nullptr) + { + LOG.Add("FECameraComponent::GetRenderTargetWidth Viewport is nullptr.", "FE_LOG_RENDERING", FE_LOG_ERROR); + return RenderTargetWidth; + } + + return static_cast(Viewport->GetWidth() * RenderScale); +} + +int FECameraComponent::GetRenderTargetHeight() const +{ + if (Viewport == nullptr) + { + LOG.Add("FECameraComponent::GetRenderTargetHeight Viewport is nullptr.", "FE_LOG_RENDERING", FE_LOG_ERROR); + return RenderTargetHeight; + } + + return static_cast(Viewport->GetHeight() * RenderScale); +} + +bool FECameraComponent::TryToSetViewportSize(const int Width, const int Height) +{ + return TryToSetViewportSizeInternal(Width, Height); +} + +bool FECameraComponent::TryToSetViewportSizeInternal(const int Width, const int Height) +{ + if (Width < 1 || Height < 1) + return false; + + if (Viewport == nullptr) + { + LOG.Add("FECameraComponent::TryToSetViewportSizeInternal Viewport is nullptr.", "FE_LOG_RENDERING", FE_LOG_ERROR); + return false; + } + + if (Viewport->GetType() == FE_VIEWPORT_VIRTUAL) + { + Viewport->SetWidth(Width); + Viewport->SetHeight(Height); + RenderScale = 1.0f; + AspectRatio = static_cast(Viewport->GetWidth()) / static_cast(Viewport->GetHeight()); + + return true; + } + else + { + // If the viewport is not virtual this function should fail. + LOG.Add("FECameraComponent::TryToSetViewportSizeInternal Viewport is not virtual.", "FE_LOG_RENDERING", FE_LOG_WARNING); + return false; + } + + return false; +} + +float FECameraComponent::GetBloomThreshold() +{ + return BloomThreshold; +} + +void FECameraComponent::SetBloomThreshold(float NewValue) +{ + BloomThreshold = NewValue; +} + +float FECameraComponent::GetBloomSize() +{ + return BloomSize; +} + +void FECameraComponent::SetBloomSize(float NewValue) +{ + BloomSize = NewValue; +} + +bool FECameraComponent::IsTemporalJitterEnabled() +{ + return bTemporalJitterEnabled; +} + +void FECameraComponent::SetTemporalJitterEnabled(const bool NewValue) +{ + bTemporalJitterEnabled = NewValue; +} + +size_t FECameraComponent::GetTemporalJitterSequenceLength() +{ + return TemporalJitterSequenceLength; +} + +void FECameraComponent::SetTemporalJitterSequenceLength(size_t NewValue) +{ + TemporalJitterSequenceLength = NewValue; +} + +glm::vec2 FECameraComponent::GetTemporalJitterOffset() +{ + if (!bTemporalJitterEnabled) + return glm::vec2(0.0f); + + return CurrentTemporalJitterOffset; +} + +// Halton jitter +void FECameraComponent::UpdateTemporalJitterOffset() +{ + if (LastTemporalFrameIndexUpdateEngineFrame == ENGINE.GetCurrentFrameIndex()) + return; + + LastTemporalFrameIndexUpdateEngineFrame = ENGINE.GetCurrentFrameIndex(); + + CurrentTemporalJitterOffset = glm::vec2(0.0f, 0.0f); + TemporalFrameIndex = (TemporalFrameIndex + 1) % TemporalJitterSequenceLength; + + constexpr int BaseX = 2; + int Index = TemporalFrameIndex + 1; + float InvBase = 1.0f / BaseX; + float Fraction = InvBase; + while (Index > 0) + { + CurrentTemporalJitterOffset.x += (Index % BaseX) * Fraction; + Index /= BaseX; + Fraction *= InvBase; + } + + constexpr int BaseY = 3; + Index = TemporalFrameIndex + 1; + InvBase = 1.0f / BaseY; + Fraction = InvBase; + while (Index > 0) + { + CurrentTemporalJitterOffset.y += (Index % BaseY) * Fraction; + Index /= BaseY; + Fraction *= InvBase; + } + + CurrentTemporalJitterOffset.x -= 0.5f; + CurrentTemporalJitterOffset.y -= 0.5f; +} + +float FECameraComponent::GetFXAASpanMax() +{ + return FXAASpanMax; +} + +void FECameraComponent::SetFXAASpanMax(float NewValue) +{ + FXAASpanMax = NewValue; +} + +float FECameraComponent::GetFXAAReduceMin() +{ + return FXAAReduceMin; +} + +void FECameraComponent::SetFXAAReduceMin(float NewValue) +{ + FXAAReduceMin = NewValue; +} + +float FECameraComponent::GetFXAAReduceMul() +{ + return FXAAReduceMul; +} + +void FECameraComponent::SetFXAAReduceMul(float NewValue) +{ + FXAAReduceMul = NewValue; +} + +float FECameraComponent::GetDOFNearDistance() +{ + return DOFNearDistance; +} + +void FECameraComponent::SetDOFNearDistance(float NewValue) +{ + DOFNearDistance = NewValue; +} + +float FECameraComponent::GetDOFFarDistance() +{ + return DOFFarDistance; +} + +void FECameraComponent::SetDOFFarDistance(float NewValue) +{ + DOFFarDistance = NewValue; +} + +float FECameraComponent::GetDOFStrength() +{ + return DOFStrength; +} + +void FECameraComponent::SetDOFStrength(float NewValue) +{ + DOFStrength = NewValue; +} + +float FECameraComponent::GetDOFDistanceDependentStrength() +{ + return DOFDistanceDependentStrength; +} + +void FECameraComponent::SetDOFDistanceDependentStrength(float NewValue) +{ + DOFDistanceDependentStrength = NewValue; +} + +float FECameraComponent::GetChromaticAberrationIntensity() +{ + return ChromaticAberrationIntensity; +} + +void FECameraComponent::SetChromaticAberrationIntensity(float NewValue) +{ + ChromaticAberrationIntensity = NewValue; +} + +bool FECameraComponent::IsSSAOEnabled() +{ + return bSSAOActive; +} + +void FECameraComponent::SetSSAOEnabled(const bool NewValue) +{ + bSSAOActive = NewValue; +} + +int FECameraComponent::GetSSAOSampleCount() +{ + return SSAOSampleCount; +} + +void FECameraComponent::SetSSAOSampleCount(int NewValue) +{ + if (NewValue < 1) + NewValue = 1; + + if (NewValue > 64) + NewValue = 64; + + SSAOSampleCount = NewValue; +} + +bool FECameraComponent::IsSSAOSmallDetailsEnabled() +{ + return bSSAOSmallDetails; +} + +void FECameraComponent::SetSSAOSmallDetailsEnabled(const bool NewValue) +{ + bSSAOSmallDetails = NewValue; +} + +bool FECameraComponent::IsSSAOResultBlured() +{ + return bSSAOBlured; +} + +void FECameraComponent::SetSSAOResultBlured(const bool NewValue) +{ + bSSAOBlured = NewValue; +} + +float FECameraComponent::GetSSAOBias() +{ + return SSAOBias; +} + +void FECameraComponent::SetSSAOBias(const float NewValue) +{ + SSAOBias = NewValue; +} + +float FECameraComponent::GetSSAORadius() +{ + return SSAORadius; +} + +void FECameraComponent::SetSSAORadius(const float NewValue) +{ + SSAORadius = NewValue; +} + +float FECameraComponent::GetSSAORadiusSmallDetails() +{ + return SSAORadiusSmallDetails; +} + +void FECameraComponent::SetSSAORadiusSmallDetails(const float NewValue) +{ + SSAORadiusSmallDetails = NewValue; +} + +float FECameraComponent::GetSSAOSmallDetailsWeight() +{ + return SSAOSmallDetailsWeight; +} + +void FECameraComponent::SetSSAOSmallDetailsWeight(const float NewValue) +{ + SSAOSmallDetailsWeight = NewValue; +} + +bool FECameraComponent::IsDistanceFogEnabled() +{ + return bDistanceFogEnabled; +} + +void FECameraComponent::SetDistanceFogEnabled(const bool NewValue) +{ + if (bDistanceFogEnabled == false && NewValue == true) + { + if (DistanceFogDensity <= 0.0f) + DistanceFogDensity = 0.007f; + if (DistanceFogGradient <= 0.0f) + DistanceFogGradient = 2.5f; + } + bDistanceFogEnabled = NewValue; +} + +float FECameraComponent::GetDistanceFogDensity() +{ + return DistanceFogDensity; +} + +void FECameraComponent::SetDistanceFogDensity(const float NewValue) +{ + DistanceFogDensity = NewValue; +} + +float FECameraComponent::GetDistanceFogGradient() +{ + return DistanceFogGradient; +} + +void FECameraComponent::SetDistanceFogGradient(const float NewValue) +{ + DistanceFogGradient = NewValue; +} + +const FEViewport* FECameraComponent::GetViewport() +{ + return Viewport; +} + +FERenderingPipeline FECameraComponent::GetRenderingPipeline() const +{ + return RenderingPipeline; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FECameraComponent.h b/SubSystems/Scene/Components/FECameraComponent.h new file mode 100644 index 0000000..1873199 --- /dev/null +++ b/SubSystems/Scene/Components/FECameraComponent.h @@ -0,0 +1,224 @@ +#pragma once +#include "../Renderer/FEViewport.h" + +namespace FocalEngine +{ + enum class FERenderingPipeline + { + Deferred, + Forward_Simplified, + }; + + struct FOCAL_ENGINE_API FECameraComponent + { +#define DEFAULT_CAMERA_CLEAR_COLOR glm::vec4(0.55f, 0.73f, 0.87f, 1.0f) + + friend class FECameraSystem; + friend class FERenderer; + + FECameraComponent(); + FECameraComponent(const FECameraComponent& Other) = default; + + bool IsMainCamera() const; + + glm::mat4 GetViewMatrix() const; + void SetViewMatrix(const glm::mat4 NewViewMatrix); + glm::mat4 GetProjectionMatrix() const; + void SetProjectionMatrix(const glm::mat4 NewProjectionMatrix); + + glm::vec3 GetUp() const; + glm::vec3 GetForward() const; + glm::vec3 GetRight() const; + + bool IsActive() const; + void SetActive(const bool Active); + + bool IsClearColorEnabled() const; + void SetIsClearColorEnabled(const bool bEnabled); + + glm::vec4 GetClearColor() const; + void SetClearColor(glm::vec4 NewClearColor); + + float GetFOV() const; + void SetFOV(const float FOV); + + float GetNearPlane() const; + void SetNearPlane(const float NearPlane); + + float GetFarPlane() const; + void SetFarPlane(const float FarPlane); + + float GetAspectRatio() const; + void SetAspectRatio(const float AspectRatio); + + float GetGamma() const; + void SetGamma(const float Gamma); + + float GetExposure() const; + void SetExposure(const float Exposure); + + void UpdateFrustumPlanes(); + std::vector> GetFrustumPlanes(); + + int GetRenderTargetWidth() const; + int GetRenderTargetHeight() const; + + float GetRenderScale(); + + bool TryToSetViewportSize(const int Width, const int Height); + + // *********** Anti-Aliasing(FXAA) *********** + float GetFXAASpanMax(); + void SetFXAASpanMax(float NewValue); + + float GetFXAAReduceMin(); + void SetFXAAReduceMin(float NewValue); + + float GetFXAAReduceMul(); + void SetFXAAReduceMul(float NewValue); + + // *********** Bloom *********** + float GetBloomThreshold(); + void SetBloomThreshold(float NewValue); + + float GetBloomSize(); + void SetBloomSize(float NewValue); + + // *********** Depth of Field *********** + float GetDOFNearDistance(); + void SetDOFNearDistance(float NewValue); + + float GetDOFFarDistance(); + void SetDOFFarDistance(float NewValue); + + float GetDOFStrength(); + void SetDOFStrength(float NewValue); + + float GetDOFDistanceDependentStrength(); + void SetDOFDistanceDependentStrength(float NewValue); + + // *********** Chromatic Aberration *********** + float GetChromaticAberrationIntensity(); + void SetChromaticAberrationIntensity(float NewValue); + + // *********** SSAO *********** + bool IsSSAOEnabled(); + void SetSSAOEnabled(const bool NewValue); + + int GetSSAOSampleCount(); + void SetSSAOSampleCount(int NewValue); + + bool IsSSAOSmallDetailsEnabled(); + void SetSSAOSmallDetailsEnabled(const bool NewValue); + + bool IsSSAOResultBlured(); + void SetSSAOResultBlured(const bool NewValue); + + float GetSSAOBias(); + void SetSSAOBias(const float NewValue); + + float GetSSAORadius(); + void SetSSAORadius(const float NewValue); + + float GetSSAORadiusSmallDetails(); + void SetSSAORadiusSmallDetails(const float NewValue); + + float GetSSAOSmallDetailsWeight(); + void SetSSAOSmallDetailsWeight(const float NewValue); + + //************** Distance Fog ************** + bool IsDistanceFogEnabled(); + void SetDistanceFogEnabled(const bool NewValue); + + float GetDistanceFogDensity(); + void SetDistanceFogDensity(const float NewValue); + + float GetDistanceFogGradient(); + void SetDistanceFogGradient(const float NewValue); + + const FEViewport* GetViewport(); + + FERenderingPipeline GetRenderingPipeline() const; + + // *********** Temporal Anti-Aliasing common *********** + bool IsTemporalJitterEnabled(); + void SetTemporalJitterEnabled(const bool NewValue); + + size_t GetTemporalJitterSequenceLength(); + void SetTemporalJitterSequenceLength(size_t NewValue); + + glm::vec2 GetTemporalJitterOffset(); + private: + bool bIsActive = false; + bool bIsMainCamera = false; + + FERenderingPipeline RenderingPipeline = FERenderingPipeline::Deferred; + FEViewport* Viewport = nullptr; + + glm::vec4 ClearColor = DEFAULT_CAMERA_CLEAR_COLOR; + bool bClearColorEnabled = true; + + float RenderScale = 1.0f; + int RenderTargetWidth = 0; + int RenderTargetHeight = 0; + + float FOV = 50.68f; + float NearPlane = 0.01f; + float FarPlane = 5000.0f; + float AspectRatio = 1.0f; + + float Gamma = 2.2f; + float Exposure = 1.0f; + + glm::mat4 ViewMatrix = glm::mat4(1.0f); + glm::mat4 ProjectionMatrix = glm::mat4(1.0f); + std::vector> Frustum; + + bool TryToSetViewportSizeInternal(const int Width, const int Height); + + // *********** Temporal Anti-Aliasing variables *********** + bool bTemporalJitterEnabled = false; + unsigned long long LastTemporalFrameIndexUpdateEngineFrame = 0; + int TemporalFrameIndex = 0; + glm::vec2 CurrentTemporalJitterOffset = glm::vec2(0.0f); + size_t TemporalJitterSequenceLength = 64; + glm::mat4 PreviousFrameViewMatrix = glm::mat4(1.0f); + + void UpdateTemporalJitterOffset(); + + // *********** Anti-Aliasing(FXAA) *********** + float FXAASpanMax = 8.0f; + float FXAAReduceMin = 1.0f / 128.0f; + float FXAAReduceMul = 0.4f; + + // *********** Bloom *********** + float BloomThreshold = 1.0f; + float BloomSize = 5.0f; + + // *********** Depth of Field *********** + float DOFStrength = 2.0f; + float DOFNearDistance = 0.0f; + float DOFFarDistance = 9000.0f; + float DOFDistanceDependentStrength = 100.0f; + + // *********** Chromatic Aberration *********** + float ChromaticAberrationIntensity = 1.0f; + + // *********** SSAO *********** + bool bSSAOActive = true; + int SSAOSampleCount = 16; + + bool bSSAOSmallDetails = true; + bool bSSAOBlured = true; + + float SSAOBias = 0.013f; + float SSAORadius = 10.0f; + float SSAORadiusSmallDetails = 0.4f; + float SSAOSmallDetailsWeight = 0.2f; + + //************** Distance Fog ************** + float DistanceFogDensity = 0.007f; + float DistanceFogGradient = 2.5f; + bool bDistanceFogEnabled = false; + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FEComponents.cpp b/SubSystems/Scene/Components/FEComponents.cpp new file mode 100644 index 0000000..da81b7d --- /dev/null +++ b/SubSystems/Scene/Components/FEComponents.cpp @@ -0,0 +1,408 @@ +#include "FEComponents.h" +#include "../FEScene.h" + +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetComponentsTools() +{ + return FEComponentsTools::GetInstancePointer(); +} +#endif + +FEComponentsTools::FEComponentsTools() +{ + // ************************* TAG COMPONENT ************************* + FEComponentTypeInfo TagComponentInfo("Tag", typeid(FETagComponent)); + FunctionsToGetEntityIDListWith[TagComponentInfo.Type] = [](FEScene* CurrentScene) { return CurrentScene->GetEntityIDListWithComponent(); }; + TagComponentInfo.bCanNotBeRemoved = true; + TagComponentInfo.ToJson = [](FEEntity* ParentEntity) -> Json::Value { + Json::Value Root; + FETagComponent& CurrentComponent = ParentEntity->GetComponent(); + Root["Tag"] = CurrentComponent.GetTag(); + return Root; + }; + + TagComponentInfo.FromJson = [](FEEntity* ParentEntity, Json::Value Root) { + FETagComponent& CurrentComponent = ParentEntity->GetComponent(); + CurrentComponent.SetTag(Root["Tag"].asString()); + }; + + TagComponentInfo.DuplicateComponent = [](FEEntity* SourceEntity, FEEntity* TargetEntity) { + TargetEntity->GetComponent() = SourceEntity->GetComponent(); + }; + TagComponentInfo.LoadingPriority = 0; + ComponentIDToInfo[entt::type_id().hash()] = TagComponentInfo; + FEComponentTypeInfo& TagInfo = ComponentIDToInfo[entt::type_id().hash()]; + + // ************************* TRANSFORM COMPONENT ************************* + FEComponentTypeInfo TransformComponentInfo("Transform", typeid(FETransformComponent)); + FunctionsToGetEntityIDListWith[TransformComponentInfo.Type] = [](FEScene* CurrentScene) { return CurrentScene->GetEntityIDListWithComponent(); }; + TransformComponentInfo.bCanNotBeRemoved = true; + TransformComponentInfo.LoadingPriority = 1; + ComponentIDToInfo[entt::type_id().hash()] = TransformComponentInfo; + FEComponentTypeInfo& TransformInfo = ComponentIDToInfo[entt::type_id().hash()]; + + // ************************* CAMERA COMPONENT ************************* + FEComponentTypeInfo CameraComponentInfo("Camera", typeid(FECameraComponent)); + FunctionsToGetEntityIDListWith[CameraComponentInfo.Type] = [](FEScene* CurrentScene) { return CurrentScene->GetEntityIDListWithComponent(); }; + ComponentIDToInfo[entt::type_id().hash()] = CameraComponentInfo; + FEComponentTypeInfo& CameraInfo = ComponentIDToInfo[entt::type_id().hash()]; + + // ************************* LIGHT COMPONENT ************************* + FEComponentTypeInfo LightComponentInfo("Light", typeid(FELightComponent)); + FunctionsToGetEntityIDListWith[LightComponentInfo.Type] = [](FEScene* CurrentScene) { return CurrentScene->GetEntityIDListWithComponent(); }; + ComponentIDToInfo[entt::type_id().hash()] = LightComponentInfo; + FEComponentTypeInfo& LightInfo = ComponentIDToInfo[entt::type_id().hash()]; + + // ************************* GAME MODEL COMPONENT ************************* + FEComponentTypeInfo GameModelComponentInfo("Game Model", typeid(FEGameModelComponent)); + FunctionsToGetEntityIDListWith[GameModelComponentInfo.Type] = [](FEScene* CurrentScene) { return CurrentScene->GetEntityIDListWithComponent(); }; + GameModelComponentInfo.ToJson = [](FEEntity* ParentEntity) -> Json::Value { + Json::Value Root; + FEGameModelComponent& CurrentComponent = ParentEntity->GetComponent(); + Root["ModelID"] = CurrentComponent.GetGameModel()->GetObjectID(); + + Root["bVisible"] = CurrentComponent.IsVisible(); + Root["bCastShadows"] = CurrentComponent.IsCastShadows(); + Root["bReceiveShadows"] = CurrentComponent.IsReceivingShadows(); + Root["bUniformLighting"] = CurrentComponent.IsUniformLighting(); + Root["bApplyPostprocess"] = CurrentComponent.IsPostprocessApplied(); + Root["bWireframeMode"] = CurrentComponent.IsWireframeMode(); + + return Root; + }; + + GameModelComponentInfo.FromJson = [](FEEntity* ParentEntity, Json::Value Root) { + ParentEntity->AddComponent(RESOURCE_MANAGER.GetGameModel(Root["ModelID"].asString())); + FEGameModelComponent& CurrentComponent = ParentEntity->GetComponent(); + + CurrentComponent.SetVisibility(Root["bVisible"].asBool()); + CurrentComponent.SetCastShadows(Root["bCastShadows"].asBool()); + CurrentComponent.SetReceivingShadows(Root["bReceiveShadows"].asBool()); + CurrentComponent.SetUniformLighting(Root["bUniformLighting"].asBool()); + CurrentComponent.SetIsPostprocessApplied(Root["bApplyPostprocess"].asBool()); + CurrentComponent.SetWireframeMode(Root["bWireframeMode"].asBool()); + }; + + GameModelComponentInfo.DuplicateComponent = [](FEEntity* SourceEntity, FEEntity* TargetEntity) { + if (TargetEntity->AddComponent()) + TargetEntity->GetComponent() = SourceEntity->GetComponent(); + }; + GameModelComponentInfo.LoadingPriority = 2; + ComponentIDToInfo[entt::type_id().hash()] = GameModelComponentInfo; + FEComponentTypeInfo& GameModelInfo = ComponentIDToInfo[entt::type_id().hash()]; + + // ************************* INSTANCED COMPONENT ************************* + FEComponentTypeInfo InstancedComponentInfo("Instanced", typeid(FEInstancedComponent)); + FunctionsToGetEntityIDListWith[InstancedComponentInfo.Type] = [](FEScene* CurrentScene) { return CurrentScene->GetEntityIDListWithComponent(); }; + InstancedComponentInfo.LoadingPriority = 3; + ComponentIDToInfo[entt::type_id().hash()] = InstancedComponentInfo; + FEComponentTypeInfo& InstancedInfo = ComponentIDToInfo[entt::type_id().hash()]; + + // ************************* TERRAIN COMPONENT ************************* + FEComponentTypeInfo TerrainComponentInfo("Terrain", typeid(FETerrainComponent)); + FunctionsToGetEntityIDListWith[TerrainComponentInfo.Type] = [](FEScene* CurrentScene) { return CurrentScene->GetEntityIDListWithComponent(); }; + ComponentIDToInfo[entt::type_id().hash()] = TerrainComponentInfo; + FEComponentTypeInfo& TerrainInfo = ComponentIDToInfo[entt::type_id().hash()]; + + // ************************* SKY DOME COMPONENT ************************* + FEComponentTypeInfo SkyDomeComponentInfo("SkyDome", typeid(FESkyDomeComponent)); + FunctionsToGetEntityIDListWith[SkyDomeComponentInfo.Type] = [](FEScene* CurrentScene) { return CurrentScene->GetEntityIDListWithComponent(); }; + ComponentIDToInfo[entt::type_id().hash()] = SkyDomeComponentInfo; + FEComponentTypeInfo& SkyDomeInfo = ComponentIDToInfo[entt::type_id().hash()]; + + // ************************* PREFAB INSTANCE COMPONENT ************************* + FEComponentTypeInfo PrefabInstanceComponentInfo("Prefab Instance", typeid(FEPrefabInstanceComponent)); + FunctionsToGetEntityIDListWith[PrefabInstanceComponentInfo.Type] = [](FEScene* CurrentScene) { return CurrentScene->GetEntityIDListWithComponent(); }; + PrefabInstanceComponentInfo.LoadingPriority = 2; + ComponentIDToInfo[entt::type_id().hash()] = PrefabInstanceComponentInfo; + FEComponentTypeInfo& PrefabInstanceInfo = ComponentIDToInfo[entt::type_id().hash()]; + + // ************************* VIRTUAL UI COMPONENT ************************* + FEComponentTypeInfo VirtualUIComponentInfo("Virtual UI", typeid(FEVirtualUIComponent)); + FunctionsToGetEntityIDListWith[VirtualUIComponentInfo.Type] = [](FEScene* CurrentScene) { return CurrentScene->GetEntityIDListWithComponent(); }; + VirtualUIComponentInfo.LoadingPriority = 3; + ComponentIDToInfo[entt::type_id().hash()] = VirtualUIComponentInfo; + FEComponentTypeInfo& VirtualUIInfo = ComponentIDToInfo[entt::type_id().hash()]; + + // ************************* NATIVE SCRIPT COMPONENT ************************* + FEComponentTypeInfo NativeScriptComponentInfo("Native Script", typeid(FENativeScriptComponent)); + FunctionsToGetEntityIDListWith[NativeScriptComponentInfo.Type] = [](FEScene* CurrentScene) { return CurrentScene->GetEntityIDListWithComponent(); }; + NativeScriptComponentInfo.LoadingPriority = 4; + ComponentIDToInfo[entt::type_id().hash()] = NativeScriptComponentInfo; + FEComponentTypeInfo& NativeScriptInfo = ComponentIDToInfo[entt::type_id().hash()]; + + // Define constraints + TagInfo.Constraints.push_back({ FE_LOGIC_OPERATION::NOT, {TagInfo} }); + TransformInfo.Constraints.push_back({ FE_LOGIC_OPERATION::NOT, {TransformInfo} }); + CameraInfo.Constraints.push_back({ FE_LOGIC_OPERATION::NOT, {CameraInfo} }); + LightInfo.Constraints.push_back({ FE_LOGIC_OPERATION::NOT, {LightInfo} }); + GameModelInfo.Constraints.push_back({ FE_LOGIC_OPERATION::NOT, {GameModelInfo} }); + + InstancedInfo.Constraints.push_back({ FE_LOGIC_OPERATION::NOT, {InstancedInfo} }); + InstancedInfo.Constraints.push_back({ FE_LOGIC_OPERATION::XOR, {GameModelInfo, PrefabInstanceComponentInfo} }); + + TerrainInfo.Constraints.push_back({ FE_LOGIC_OPERATION::NOT, {TerrainInfo} }); + SkyDomeInfo.Constraints.push_back({ FE_LOGIC_OPERATION::NOT, {SkyDomeInfo} }); + PrefabInstanceInfo.Constraints.push_back({ FE_LOGIC_OPERATION::NOT, {PrefabInstanceInfo} }); + VirtualUIInfo.Constraints.push_back({ FE_LOGIC_OPERATION::NOT, {VirtualUIInfo, GameModelInfo} }); + // TO_DO: Maybe somehow allow multiple native script components on one entity. + NativeScriptInfo.Constraints.push_back({ FE_LOGIC_OPERATION::NOT, {NativeScriptInfo} }); +} + +std::vector FEComponentsTools::GetComponentInfoList() +{ + std::vector Result; + for (const auto& Component : ComponentIDToInfo) + Result.push_back(Component.second); + + return Result; +} + +std::vector FEComponentsTools::GetEntityIDListWithComponent(FEScene* CurrentScene, const FEComponentTypeInfo& ComponentInfo) +{ + if (FunctionsToGetEntityIDListWith.find(ComponentInfo.Type) == FunctionsToGetEntityIDListWith.end()) + { + LOG.Add("Function to get entity id list with component not found in FEComponentsTools::GetEntityIDListWithComponent", "FE_LOG_ECS", FE_LOG_ERROR); + return {}; + } + + return FunctionsToGetEntityIDListWith[ComponentInfo.Type](CurrentScene); +} + +FEComponentTypeInfo* FEComponentsTools::GetComponentInfoByName(std::string Name) +{ + for (auto& Component : ComponentIDToInfo) + { + if (Component.second.Name == Name) + return &Component.second; + } + + return nullptr; +} + +void FEComponentsTools::SortComponentsByLoadingPriority(std::vector& Components) +{ + std::sort(Components.begin(), Components.end(), [](const FEComponentTypeInfo& FirstComponent, const FEComponentTypeInfo& SecondComponent) -> bool { + int FirstPriority = FirstComponent.LoadingPriority; + int SecondPriority = SecondComponent.LoadingPriority; + + bool bResult = FirstPriority < SecondPriority; + return bResult; + }); +} +void FEComponentsTools::SortComponentsByLoadingPriority(std::vector& ComponentsNames) +{ + std::sort(ComponentsNames.begin(), ComponentsNames.end(), [](const std::string& FirstComponent, const std::string& SecondComponent) -> bool { + + if (COMPONENTS_TOOL.GetComponentInfoByName(FirstComponent) == nullptr) + { + LOG.Add("Component info not found for component: " + FirstComponent, "FE_LOG_ECS", FE_LOG_ERROR); + return false; + } + + if (COMPONENTS_TOOL.GetComponentInfoByName(SecondComponent) == nullptr) + { + LOG.Add("Component info not found for component: " + SecondComponent, "FE_LOG_ECS", FE_LOG_ERROR); + return false; + } + + int FirstPriority = COMPONENTS_TOOL.GetComponentInfoByName(FirstComponent)->LoadingPriority; + int SecondPriority = COMPONENTS_TOOL.GetComponentInfoByName(SecondComponent)->LoadingPriority; + + bool bResult = FirstPriority < SecondPriority; + return bResult; + }); +} + +FEComponentTypeInfo::FEComponentTypeInfo() : Name(), Type(nullptr) {} +FEComponentTypeInfo::FEComponentTypeInfo(const std::string& Name, const std::type_info& Type) +{ + this->Name = Name; + this->Type = &Type; +} + +bool FEComponentTypeInfo::CanBeAddedToEntity(FEEntity* PotentialParentEntity, std::string* ErrorMessage) +{ + std::string LocalErrorMessage; + if (ErrorMessage != nullptr) + *ErrorMessage = ""; + + if (PotentialParentEntity == nullptr) + { + LOG.Add("ProspectParentEntity is nullptr in FEComponentTypeInfo::CanBeAddedToEntity", "FE_LOG_ECS", FE_LOG_ERROR); + if (ErrorMessage != nullptr) + *ErrorMessage = "ProspectParentEntity is nullptr"; + + return false; + } + + if (PotentialParentEntity->GetParentScene() == nullptr) + { + LOG.Add("ProspectParentEntity parent scene is nullptr in FEComponentTypeInfo::CanBeAddedToEntity", "FE_LOG_ECS", FE_LOG_ERROR); + if (ErrorMessage != nullptr) + *ErrorMessage = "ProspectParentEntity parent scene is nullptr"; + + return false; + } + + FEScene* ParentScene = PotentialParentEntity->GetParentScene(); + if (MaxSceneComponentCount != -1) + { + size_t CurrentComponentCount = COMPONENTS_TOOL.GetEntityIDListWithComponent(ParentScene, *this).size(); + if (CurrentComponentCount >= MaxSceneComponentCount) + { + LOG.Add("Max component count reached in FEComponentTypeInfo::CanBeAddedToEntity", "FE_LOG_ECS", FE_LOG_WARNING); + + if (ErrorMessage != nullptr) + *ErrorMessage = "Max component count reached"; + + return false; + } + } + + std::vector CurrentlyExistingComponents = PotentialParentEntity->GetComponentsInfoList(); + + // First we are checking if adding this component will not break any restrictions of current component + bool bNewComponentEvaluation = EvaluateConstraints(CurrentlyExistingComponents, ErrorMessage); + if (!bNewComponentEvaluation) + return false; + + // Then we need to check that other components evaluations are not broken by adding this component + for (size_t i = 0; i < CurrentlyExistingComponents.size(); i++) + { + // For each already existing component we need to create a list of components that will be evaluated. + // That list should not contain the component that we are currently querying with .EvaluateConstraints. + std::vector NewComponentList = CurrentlyExistingComponents; + NewComponentList.erase(NewComponentList.begin() + i); + NewComponentList.push_back(*this); + + if (!CurrentlyExistingComponents[i].EvaluateConstraints(NewComponentList, ErrorMessage)) + { + if (ErrorMessage != nullptr) + *ErrorMessage = "Adding this component will break restrictions of " + CurrentlyExistingComponents[i].Name; + + return false; + } + } + + return true; +} + +bool FEComponentTypeInfo::CanBeRemovedFromEntity(FEEntity* ParentEntity, std::string* ErrorMessage) +{ + std::string LocalErrorMessage; + if (ErrorMessage != nullptr) + *ErrorMessage = ""; + + if (bCanNotBeRemoved) + { + if (ErrorMessage != nullptr) + *ErrorMessage = "Can not be removed"; + + return false; + } + + if (ParentEntity == nullptr) + { + LOG.Add("ParentEntity is nullptr in FEComponentTypeInfo::CanBeRemovedFromEntity", "FE_LOG_ECS", FE_LOG_ERROR); + if (ErrorMessage != nullptr) + *ErrorMessage = "ParentEntity is nullptr"; + + return false; + } + + if (ParentEntity->GetParentScene() == nullptr) + { + LOG.Add("ParentEntity parent scene is nullptr in FEComponentTypeInfo::CanBeRemovedFromEntity", "FE_LOG_ECS", FE_LOG_ERROR); + if (ErrorMessage != nullptr) + *ErrorMessage = "ParentEntity parent scene is nullptr"; + + return false; + } + + FEScene* ParentScene = ParentEntity->GetParentScene(); + std::vector CurrentlyExistingComponents = ParentEntity->GetComponentsInfoList(); + CurrentlyExistingComponents.erase(std::remove(CurrentlyExistingComponents.begin(), CurrentlyExistingComponents.end(), *this), CurrentlyExistingComponents.end()); + + // After we remove the component we need to check if the remaining components are still valid after the removal. + // Then we need to check that other components evaluations are not broken by adding this component + for (size_t i = 0; i < CurrentlyExistingComponents.size(); i++) + { + // For each already existing component we need to create a list of components that will be evaluated. + // That list should not contain the component that we are currently querying with .EvaluateConstraints. + std::vector NewComponentList = CurrentlyExistingComponents; + NewComponentList.erase(NewComponentList.begin() + i); + + if (!CurrentlyExistingComponents[i].EvaluateConstraints(NewComponentList, ErrorMessage)) + { + if (ErrorMessage != nullptr) + *ErrorMessage = "Removing this component will break restrictions of " + CurrentlyExistingComponents[i].Name; + + return false; + } + } + + return true; +} + +bool FEComponentTypeInfo::EvaluateConstraints(const std::vector& ComponentToCheckTowards, std::string* ErrorMessage) const +{ + for (const auto& CurrentConstraint : Constraints) + { + if (!EvaluateConstraint(CurrentConstraint, ComponentToCheckTowards)) + { + if (ErrorMessage) + { + *ErrorMessage = "Constraint failed: "; + for (const auto& CurrentComponent : CurrentConstraint.Components) + *ErrorMessage += CurrentComponent.Name + ", "; + + *ErrorMessage += "with operation " + LogicOperationToString(CurrentConstraint.LogicOperation); + } + return false; + } + } + return true; +} + +bool FEComponentTypeInfo::EvaluateConstraint(const FEComponentConstraint& Constraint, const std::vector& ComponentToCheckTowards) const +{ + auto ComponentExists = [&ComponentToCheckTowards](const FEComponentTypeInfo& Component) { + return std::find(ComponentToCheckTowards.begin(), ComponentToCheckTowards.end(), Component) != ComponentToCheckTowards.end(); + }; + + switch (Constraint.LogicOperation) + { + case FE_LOGIC_OPERATION::AND: + return std::all_of(Constraint.Components.begin(), Constraint.Components.end(), ComponentExists); + case FE_LOGIC_OPERATION::OR: + return std::any_of(Constraint.Components.begin(), Constraint.Components.end(), ComponentExists); + case FE_LOGIC_OPERATION::NOT: + return std::none_of(Constraint.Components.begin(), Constraint.Components.end(), ComponentExists); + case FE_LOGIC_OPERATION::XOR: + return std::count_if(Constraint.Components.begin(), Constraint.Components.end(), ComponentExists) == 1; + default: + return false; + } +} + +std::string FEComponentTypeInfo::LogicOperationToString(FE_LOGIC_OPERATION LogicOperation) const +{ + switch (LogicOperation) + { + case FocalEngine::FE_LOGIC_OPERATION::AND: + return "AND"; + case FocalEngine::FE_LOGIC_OPERATION::OR: + return "OR"; + case FocalEngine::FE_LOGIC_OPERATION::NOT: + return "NOT"; + case FocalEngine::FE_LOGIC_OPERATION::XOR: + return "XOR"; + default: + break; + } + + return "UNKNOWN"; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FEComponents.h b/SubSystems/Scene/Components/FEComponents.h new file mode 100644 index 0000000..d52fae6 --- /dev/null +++ b/SubSystems/Scene/Components/FEComponents.h @@ -0,0 +1,125 @@ +#pragma once +#include "FETagComponent.h" +#include "FETransformComponent.h" +#include "FECameraComponent.h" +#include "FELightComponent.h" +#include "FEGameModelComponent.h" +#include "FEInstancedComponent.h" +#include "FETerrainComponent.h" +#include "FESkyDomeComponent.h" +#include "FEPrefabInstanceComponent.h" +#include "FEVirtualUIComponent.h" +#include "NativeScriptSystem/FENativeScriptComponent.h" + +#include "entt.hpp" + +namespace FocalEngine +{ + enum class FE_LOGIC_OPERATION + { + AND, + OR, + NOT, + XOR + }; + + struct FEComponentTypeInfo; + struct FEComponentConstraint + { + FE_LOGIC_OPERATION LogicOperation; + std::vector Components; + }; + + // Define a struct to hold component type information + struct FEComponentTypeInfo + { + std::string Name; + const std::type_info* Type; + + std::vector Constraints; + int MaxSceneComponentCount = -1; + bool bCanNotBeRemoved = false; + // That variable defines the priority of loading the component. Relevant for the entities components list. + // The lower the value, the earlier the component will be loaded. + int LoadingPriority = INT_MAX; + + FEComponentTypeInfo(); + FEComponentTypeInfo(const std::string& Name, const std::type_info& Type); + + bool operator==(const FEComponentTypeInfo& Other) const + { + return *Type == *Other.Type; + } + + bool CanBeAddedToEntity(FEEntity* PotentialParentEntity, std::string* ErrorMessage = nullptr); + bool CanBeRemovedFromEntity(FEEntity* ParentEntity, std::string* ErrorMessage = nullptr); + + std::function DuplicateComponent = nullptr; + std::function ToJson = nullptr; + std::function FromJson = nullptr; + + bool EvaluateConstraints(const std::vector& ComponentToCheckTowards, std::string* ErrorMessage) const; + bool EvaluateConstraint(const FEComponentConstraint& Constraint, const std::vector& ComponentToCheckTowards) const; + private: + std::string LogicOperationToString(FE_LOGIC_OPERATION LogicOperation) const; + }; + + class FOCAL_ENGINE_API FEComponentsTools + { + SINGLETON_PRIVATE_PART(FEComponentsTools) + + friend class FEEntity; + + friend class FETransformSystem; + friend class FECameraSystem; + friend class FELightSystem; + friend class FEInstancedSystem; + friend class FETerrainSystem; + friend class FESkyDomeSystem; + friend class FEPrefabInstanceSystem; + friend class FEVirtualUISystem; + friend class FENativeScriptSystem; + + std::map ComponentIDToInfo; + std::map(FEScene*)>> FunctionsToGetEntityIDListWith; + + template + void RegisterComponentToJsonFunction(std::function Function) + { + ComponentIDToInfo[entt::type_id().hash()].ToJson = Function; + } + + template + void RegisterComponentFromJsonFunction(std::function Function) + { + ComponentIDToInfo[entt::type_id().hash()].FromJson = Function; + } + + template + void RegisterComponentDuplicateFunction(std::function Function) + { + ComponentIDToInfo[entt::type_id().hash()].DuplicateComponent = Function; + } + public: + SINGLETON_PUBLIC_PART(FEComponentsTools) + + std::vector GetComponentInfoList(); + std::vector GetEntityIDListWithComponent(FEScene* CurrentScene, const FEComponentTypeInfo& ComponentInfo); + FEComponentTypeInfo* GetComponentInfoByName(std::string Name); + template + FEComponentTypeInfo* GetComponentInfo() + { + return &ComponentIDToInfo[entt::type_id().hash()]; + } + + void SortComponentsByLoadingPriority(std::vector& Components); + void SortComponentsByLoadingPriority(std::vector& ComponentsNames); + }; + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetComponentsTools(); + #define COMPONENTS_TOOL (*static_cast(GetComponentsTools())) +#else + #define COMPONENTS_TOOL FEComponentsTools::GetInstance() +#endif +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FEGameModelComponent.cpp b/SubSystems/Scene/Components/FEGameModelComponent.cpp new file mode 100644 index 0000000..6309fe7 --- /dev/null +++ b/SubSystems/Scene/Components/FEGameModelComponent.cpp @@ -0,0 +1,99 @@ +#include "FEGameModelComponent.h" +using namespace FocalEngine; + +FEGameModelComponent::FEGameModelComponent(FEGameModel* GameModelToSet) +{ + GameModel = GameModelToSet; +} + +FEGameModelComponent::FEGameModelComponent(const FEGameModelComponent& Other) +{ + GameModel = Other.GameModel; + bVisible = Other.bVisible; + bCastShadows = Other.bCastShadows; + bReceiveShadows = Other.bReceiveShadows; + bUniformLighting = Other.bUniformLighting; + bApplyPostprocess = Other.bApplyPostprocess; + bWireframeMode = Other.bWireframeMode; +} + +void FEGameModelComponent::operator=(const FEGameModelComponent& Other) +{ + GameModel = Other.GameModel; + bVisible = Other.bVisible; + bCastShadows = Other.bCastShadows; + bReceiveShadows = Other.bReceiveShadows; + bUniformLighting = Other.bUniformLighting; + bApplyPostprocess = Other.bApplyPostprocess; + bWireframeMode = Other.bWireframeMode; +} + +bool FEGameModelComponent::IsVisible() const +{ + return bVisible; +} + +void FEGameModelComponent::SetVisibility(bool NewValue) +{ + bVisible = NewValue; +} + +bool FEGameModelComponent::IsCastShadows() const +{ + return bCastShadows; +} + +void FEGameModelComponent::SetCastShadows(bool NewValue) +{ + bCastShadows = NewValue; +} + +bool FEGameModelComponent::IsReceivingShadows() const +{ + return bReceiveShadows; +} + +void FEGameModelComponent::SetReceivingShadows(bool NewValue) +{ + bReceiveShadows = NewValue; +} + +bool FEGameModelComponent::IsUniformLighting() const +{ + return bUniformLighting; +} + +void FEGameModelComponent::SetUniformLighting(bool NewValue) +{ + bUniformLighting = NewValue; +} + +bool FEGameModelComponent::IsPostprocessApplied() const +{ + return bApplyPostprocess; +} + +void FEGameModelComponent::SetIsPostprocessApplied(bool NewValue) +{ + bApplyPostprocess = NewValue; +} + +bool FEGameModelComponent::IsWireframeMode() const +{ + return bWireframeMode; +} + +void FEGameModelComponent::SetWireframeMode(bool NewValue) +{ + bWireframeMode = NewValue; +} + +FEGameModel* FEGameModelComponent::GetGameModel() +{ + return GameModel; +} + +void FEGameModelComponent::SetGameModel(FEGameModel* NewGameModel) +{ + GameModel = NewGameModel; +} \ No newline at end of file diff --git a/Renderer/FEEntity.h b/SubSystems/Scene/Components/FEGameModelComponent.h similarity index 57% rename from Renderer/FEEntity.h rename to SubSystems/Scene/Components/FEGameModelComponent.h index 2503d93..a470b87 100644 --- a/Renderer/FEEntity.h +++ b/SubSystems/Scene/Components/FEGameModelComponent.h @@ -1,34 +1,18 @@ #pragma once - -#ifndef FEENTITY_H -#define FEENTITY_H - -#include "FEFramebuffer.h" -#include "FEPrefab.h" +#include "../Renderer/FEGameModel.h" namespace FocalEngine { - class FEEntity : public FEObject + struct FEGameModelComponent { - friend class FEEntityInstanced; - friend class FERenderer; - friend class FEResourceManager; - friend class FEScene; - - FEEntity(); - FEEntity(FEPrefab* Prefab, std::string Name); - ~FEEntity(); - public: - FEPrefab* Prefab = nullptr; - FETransformComponent Transform; - - virtual void Render(); + FEGameModelComponent() {}; + FEGameModelComponent(FEGameModel* GameModelToSet); + FEGameModelComponent(const FEGameModelComponent& Other); + void operator=(const FEGameModelComponent& Other); bool IsVisible() const; void SetVisibility(bool NewValue); - virtual FEAABB GetAABB(); - bool IsCastShadows() const; void SetCastShadows(bool NewValue); @@ -43,16 +27,17 @@ namespace FocalEngine bool IsWireframeMode() const; void SetWireframeMode(bool NewValue); - protected: + + FEGameModel* GetGameModel(); + void SetGameModel(FEGameModel* NewGameModel); + private: + FEGameModel* GameModel = nullptr; + bool bVisible = true; bool bCastShadows = true; bool bReceiveShadows = true; bool bUniformLighting = false; bool bApplyPostprocess = true; bool bWireframeMode = false; - - FEAABB EntityAABB; }; -} - -#endif FEENTITY_H \ No newline at end of file +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FEInstancedComponent.cpp b/SubSystems/Scene/Components/FEInstancedComponent.cpp new file mode 100644 index 0000000..5ee2747 --- /dev/null +++ b/SubSystems/Scene/Components/FEInstancedComponent.cpp @@ -0,0 +1,170 @@ +#include "FEInstancedComponent.h" +using namespace FocalEngine; + +float FESpawnInfo::GetMinScale() +{ + return MinScale; +} + +void FESpawnInfo::SetMinScale(const float NewValue) +{ + if (NewValue >= MaxScale) + return; + + MinScale = NewValue; +} + +float FESpawnInfo::GetMaxScale() +{ + return MaxScale; +} + +void FESpawnInfo::SetMaxScale(const float NewValue) +{ + if (NewValue <= MinScale) + return; + + MaxScale = NewValue; +} + +float FESpawnInfo::GetPositionDeviation() +{ + const int IntegerPart = rand() % static_cast(Radius); + const float FractionalPart = static_cast((rand() % 100) / 100.0f); + float Result = static_cast(IntegerPart) + FractionalPart; + + Result -= Radius / 2.0f; + + return Result; +} + +float FESpawnInfo::GetScaleDeviation() +{ + const float FinalDeviation = MinScale + ((static_cast(rand() % static_cast((MaxScale - MinScale) * 10000)) / 10000.0f)); + return FinalDeviation; +} + +int FESpawnInfo::GetRotaionDeviation(const glm::vec3 Axis) +{ + if (Axis.x > 0.0f) + { + const int RotationAngle = static_cast(360 * RotationDeviation.x); + if (RotationAngle == 0) + return 0; + return rand() % RotationAngle; + } + else if (Axis.y > 0.0f) + { + const int RotationAngle = static_cast(360 * RotationDeviation.y); + if (RotationAngle == 0) + return 0; + return rand() % RotationAngle; + } + else + { + const int RotationAngle = static_cast(360 * RotationDeviation.z); + if (RotationAngle == 0) + return 0; + return rand() % RotationAngle; + } +} + +FEInstancedComponent::FEInstancedComponent() +{ + +} + +FEInstancedComponent::~FEInstancedComponent() +{ + //delete[] LODCounts; +} + +size_t FEInstancedComponent::GetInstanceCount() +{ + return InstanceCount; +} + +void FEInstancedComponent::Clear() +{ + InstanceCount = 0; + + for (size_t i = 0; i < InstancedElementsData.size(); i++) + { + delete[] InstancedElementsData[i]->LODCounts; + + InstancedElementsData[i]->InstancedAABBSizes.resize(0); + InstancedElementsData[i]->InstancedMatrices.resize(0); + InstancedElementsData[i]->TransformedInstancedMatrices.resize(0); + InstancedElementsData[i]->InstancePositions.resize(0); + + delete InstancedElementsData[i]; + } + InstancedElementsData.resize(0); + + Modifications.clear(); +} + +FEEntity* FEInstancedComponent::GetSnappedToTerrain() +{ + return TerrainToSnap; +} + +int FEInstancedComponent::GetTerrainLayer() +{ + return TerrainLayer; +} + +float FEInstancedComponent::GetMinimalLayerIntensityToSpawn() +{ + return MinLayerIntensityToSpawn; +} + +void FEInstancedComponent::SetMinimalLayerIntensityToSpawn(float NewValue) +{ + if (NewValue < 0.0001f) + NewValue = 0.0001f; + + if (NewValue > 1.0f) + NewValue = 1.0f; + + MinLayerIntensityToSpawn = NewValue; +} + +void FEInstancedComponent::SnapToTerrain(FEEntity* Terrain) +{ + TerrainToSnap = Terrain; +} + +void FEInstancedComponent::UnSnapFromTerrain() +{ + TerrainToSnap = nullptr; +} + +void FEInstancedComponent::ConnectToTerrainLayer(FEEntity* Terrain, int LayerIndex) +{ + TerrainToSnap = Terrain; + TerrainLayer = LayerIndex; +} + +void FEInstancedComponent::UnConnectFromTerrainLayer() +{ + TerrainLayer = -1; +} + +glm::mat4 FEInstancedComponent::GetTransformedInstancedMatrix(size_t InstanceIndex) +{ + if (InstanceIndex < 0 || InstanceIndex >= InstancedElementsData[0]->TransformedInstancedMatrices.size()) + return glm::identity(); + + return InstancedElementsData[0]->TransformedInstancedMatrices[InstanceIndex]; +} + +size_t FEInstancedComponent::GetSpawnModificationCount() +{ + return Modifications.size(); +} + +std::vector FEInstancedComponent::GetSpawnModifications() +{ + return Modifications; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FEInstancedComponent.h b/SubSystems/Scene/Components/FEInstancedComponent.h new file mode 100644 index 0000000..ac85559 --- /dev/null +++ b/SubSystems/Scene/Components/FEInstancedComponent.h @@ -0,0 +1,169 @@ +#pragma once +#include "../Renderer/FEGameModel.h" + +namespace FocalEngine +{ + enum FE_INSTANCED_CHANGES_TYPE + { + FE_CHANGE_NONE = 0, + FE_CHANGE_DELETED = 1, + FE_CHANGE_MODIFIED = 2, + FE_CHANGE_ADDED = 3 + }; + + struct FEInstanceModification + { + FE_INSTANCED_CHANGES_TYPE Type = FE_CHANGE_NONE; + int Index = -1; + glm::mat4 Modification; + + FEInstanceModification(); + FEInstanceModification(const FE_INSTANCED_CHANGES_TYPE Type, const int Index, const glm::mat4 Modification) : Type(Type), Index(Index), Modification(Modification) {}; + }; + + struct FESpawnInfo + { + int Seed = 0; + int ObjectCount = 1; + float Radius = 1.0f; + + float GetMinScale(); + void SetMinScale(float NewValue); + + float GetMaxScale(); + void SetMaxScale(float NewValue); + + glm::vec3 RotationDeviation = glm::vec3(0.02f, 1.0f, 0.02f); + + float GetPositionDeviation(); + float GetScaleDeviation(); + int GetRotaionDeviation(glm::vec3 Axis); + + private: + float MinScale = 1.0f; + float MaxScale = 1.5f; + }; + + struct FEDrawElementsIndirectCommand + { + unsigned int Count; + unsigned int PrimCount; + unsigned int FirstIndex; + unsigned int BaseVertex; + unsigned int BaseInstance; + }; + + struct FEInstancedElementData + { + std::vector> InstancedMatricesLOD; + std::vector InstancePositions; + int* LODCounts = nullptr; + + // GPU Culling + GLenum InstancedBuffer = 0; + GLenum* LODBuffers = nullptr; + + GLuint SourceDataBuffer = 0; + GLuint PositionsBuffer = 0; + GLuint AABBSizesBuffer = 0; + GLuint LODInfoBuffer = 0; + + FEDrawElementsIndirectCommand* IndirectDrawsInfo; + GLuint IndirectDrawInfoBuffer = 0; + + FEAABB AllInstancesAABB; + FEGameModel* LastFrameGameModel = nullptr; + + std::vector InstancedMatrices; + std::vector TransformedInstancedMatrices; + std::vector InstancedAABBSizes; + + std::string EntityIDWithGameModelComponent = ""; + }; + + struct FEInstancedComponent + { + friend class FETerrainSystem; + friend class FEInstancedSystem; + friend class FERenderer; + friend class FEScene; + + FEInstancedComponent(); + FEInstancedComponent(const FEInstancedComponent& Other) = default; + ~FEInstancedComponent(); + + void Clear(); + + size_t GetInstanceCount(); + + FEEntity* GetSnappedToTerrain(); + int GetTerrainLayer(); + + // Later there should be a component for this + float GetMinimalLayerIntensityToSpawn(); + void SetMinimalLayerIntensityToSpawn(float NewValue); + + // Used only in editor select mode + // editor functionality, should not be here. + std::vector IndividualInstancedAABB; + + glm::mat4 GetTransformedInstancedMatrix(size_t InstanceIndex); + + size_t GetSpawnModificationCount(); + std::vector GetSpawnModifications(); + FESpawnInfo SpawnInfo; + + std::vector InstancedElementsData; + private: + bool bDirtyFlag = false; + size_t InstanceCount = 0; + + // Editor functionality, should not be here + bool bSelectionMode = false; + + // Data for postponed spawning + std::string PostponedTerrainToSnapID = ""; + int PostponedTerrainLayer = -1; + Json::Value PostponedModificationsData; + + FEEntity* TerrainToSnap = nullptr; + + int TerrainLayer = -1; + float MinLayerIntensityToSpawn = 0.4f; + + void SnapToTerrain(FEEntity* Terrain); + void UnSnapFromTerrain(); + + void ConnectToTerrainLayer(FEEntity* Terrain, int LayerIndex); + void UnConnectFromTerrainLayer(); + + FE_CULING_TYPE CullingType = FE_CULLING_LODS; + // ******************* RENDERING ******************* + //std::vector> InstancedMatricesLOD; + //std::vector InstancePositions; + //int* LODCounts = nullptr; + + //// GPU Culling + //GLenum InstancedBuffer = 0; + //GLenum* LODBuffers = nullptr; + + //GLuint SourceDataBuffer = 0; + //GLuint PositionsBuffer = 0; + //GLuint AABBSizesBuffer = 0; + //GLuint LODInfoBuffer = 0; + + //FEDrawElementsIndirectCommand* IndirectDrawsInfo; + //GLuint IndirectDrawInfoBuffer = 0; + + //FEAABB AllInstancesAABB; + //FEGameModel* LastFrameGameModel = nullptr; + + //std::vector InstancedMatrices; + //std::vector TransformedInstancedMatrices; + //std::vector InstancedAABBSizes; + // ******************* RENDERING END ******************* + + + std::vector Modifications; + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FELightComponent.cpp b/SubSystems/Scene/Components/FELightComponent.cpp new file mode 100644 index 0000000..5de29b2 --- /dev/null +++ b/SubSystems/Scene/Components/FELightComponent.cpp @@ -0,0 +1,258 @@ +#include "FELightComponent.h" +using namespace FocalEngine; + +FELightComponent::FELightComponent() +{ + Type = FE_NULL_LIGHT; +} + +FELightComponent::FELightComponent(FE_LIGHT_TYPE Type) +{ + this->Type = Type; +} + +FELightComponent::~FELightComponent() {} + +FE_LIGHT_TYPE FELightComponent::GetType() +{ + return Type; +} + +glm::vec3 FELightComponent::GetColor() +{ + return Color; +} + +void FELightComponent::SetColor(const glm::vec3 NewValue) +{ + Color = NewValue; +} + +bool FELightComponent::IsLightEnabled() +{ + return bEnabled; +} + +void FELightComponent::SetLightEnabled(const bool NewValue) +{ + bEnabled = NewValue; +} + +float FELightComponent::GetShadowBias() +{ + return ShadowBias; +} + +void FELightComponent::SetShadowBias(const float NewValue) +{ + ShadowBias = NewValue; +} + +float FELightComponent::GetIntensity() +{ + return Intensity; +} + +void FELightComponent::SetIntensity(const float NewValue) +{ + Intensity = NewValue; +} + +float FELightComponent::GetRange() +{ + return Range; +} + +void FELightComponent::SetRange(const float NewValue) +{ + Range = NewValue; +} + +float FELightComponent::GetShadowBlurFactor() +{ + return ShadowBlurFactor; +} + +void FELightComponent::SetShadowBlurFactor(float NewValue) +{ + if (NewValue < 0.0f) + NewValue = 0.0f; + + if (NewValue > 16.0f) + NewValue = 16.0f; + + ShadowBlurFactor = NewValue; +} + +bool FELightComponent::IsCastShadows() +{ + return bCastShadows; +} + +void FELightComponent::SetCastShadows(const bool NewValue) +{ + bCastShadows = NewValue; + if (!NewValue && Type == FE_DIRECTIONAL_LIGHT) + { + for (size_t i = 0; i < static_cast(ActiveCascades); i++) + { + CascadeData[i].FrameBuffer->Bind(); + FE_GL_ERROR(glClear(GL_DEPTH_BUFFER_BIT)); + CascadeData[i].FrameBuffer->UnBind(); + } + } +} + +bool FELightComponent::IsStaticShadowBias() +{ + return bStaticShadowBias; +} + +void FELightComponent::SetIsStaticShadowBias(const bool NewValue) +{ + bStaticShadowBias = NewValue; +} + +float FELightComponent::GetShadowBiasVariableIntensity() +{ + return ShadowBiasVariableIntensity; +} + +void FELightComponent::SetShadowBiasVariableIntensity(float NewValue) +{ + if (NewValue <= 0.0f) + NewValue = 0.01f; + + ShadowBiasVariableIntensity = NewValue; +} + +float FELightComponent::GetSpotAngle() +{ + return SpotAngle; +} + +void FELightComponent::SetSpotAngle(const float NewValue) +{ + SpotAngle = NewValue; +} + +float FELightComponent::GetSpotAngleOuter() +{ + return SpotAngleOuter; +} + +void FELightComponent::SetSpotAngleOuter(const float NewValue) +{ + SpotAngleOuter = NewValue; +} + +int FELightComponent::GetActiveCascades() +{ + return ActiveCascades; +} + +void FELightComponent::SetActiveCascades(int NewValue) +{ + if (NewValue < 1 || NewValue > 4) + NewValue = 1; + + ActiveCascades = NewValue; +} + +float FELightComponent::GetShadowCoverage() +{ + return ShadowCoverage; +} + +void FELightComponent::SetShadowCoverage(float NewValue) +{ + if (NewValue <= 0.0f) + NewValue = 0.1f; + + ShadowCoverage = NewValue; +} + +float FELightComponent::GetCSMZDepth() +{ + return CSMZDepth; +} + +void FELightComponent::SetCSMZDepth(float NewValue) +{ + if (NewValue <= 0.5f) + NewValue = 0.5f; + + CSMZDepth = NewValue; +} + +float FELightComponent::GetCSMXYDepth() +{ + return CSMXYDepth; +} + +void FELightComponent::SetCSMXYDepth(float NewValue) +{ + if (NewValue <= 0.5f) + NewValue = 0.5f; + + CSMXYDepth = NewValue; +} + +FECascadeData::FECascadeData() +{ + Frustum = new float* [6]; + for (size_t i = 0; i < 6; i++) + { + Frustum[i] = new float[4]; + } +} + +FECascadeData::FECascadeData(const FECascadeData& Other) +{ + Size = Other.Size; + ProjectionMat = Other.ProjectionMat; + ViewMat = Other.ViewMat; + FrameBuffer = Other.FrameBuffer; + + Frustum = new float* [6]; + for (size_t i = 0; i < 6; i++) + { + Frustum[i] = new float[4]; + } + + for (size_t i = 0; i < 6; i++) + { + for (size_t j = 0; j < 4; j++) + { + Frustum[i][j] = Other.Frustum[i][j]; + } + } +} + +void FECascadeData::operator=(const FECascadeData& Other) +{ + Size = Other.Size; + ProjectionMat = Other.ProjectionMat; + ViewMat = Other.ViewMat; + FrameBuffer = Other.FrameBuffer; + + for (size_t i = 0; i < 6; i++) + { + for (size_t j = 0; j < 4; j++) + { + Frustum[i][j] = Other.Frustum[i][j]; + } + } +} + +FECascadeData::~FECascadeData() +{ + if (Frustum == nullptr) + return; + + for (size_t i = 0; i < 6; i++) + { + delete[] Frustum[i]; + } + delete[] Frustum; +} \ No newline at end of file diff --git a/Renderer/FELight.h b/SubSystems/Scene/Components/FELightComponent.h similarity index 60% rename from Renderer/FELight.h rename to SubSystems/Scene/Components/FELightComponent.h index 20aad1d..dbf517c 100644 --- a/Renderer/FELight.h +++ b/SubSystems/Scene/Components/FELightComponent.h @@ -1,17 +1,23 @@ #pragma once - -#include "FEFramebuffer.h" -#include "../SubSystems/Scene/FETransformComponent.h" +#include "../Renderer/FEFramebuffer.h" namespace FocalEngine { + enum FE_LIGHT_TYPE + { + FE_NULL_LIGHT = 0, + FE_DIRECTIONAL_LIGHT = 1, + FE_POINT_LIGHT = 2, + FE_SPOT_LIGHT = 3 + }; + struct FELightShaderInfo { - glm::vec3 TypeAndAngles = glm::vec3(-1.0f); - glm::vec4 Position; - glm::vec4 Color; - glm::vec4 Direction; - glm::mat4 LightSpace; + glm::vec4 TypeAndAngles = glm::vec4(-1.0f); + glm::vec4 Position = glm::vec4(0.0f); + glm::vec4 Color = glm::vec4(0.0f); + glm::vec4 Direction = glm::vec4(0.0f); + glm::mat4 LightSpace = glm::mat4(0.0f); }; struct FEDirectionalLightShaderInfo @@ -30,16 +36,34 @@ namespace FocalEngine float Intensity; }; - class FEScene; - class FERenderer; - - class FELight : public FEObject + struct FECascadeData { friend FEScene; friend FERenderer; public: - FELight(FE_OBJECT_TYPE LightType); - ~FELight(); + FECascadeData(); + FECascadeData(const FECascadeData& Other); + void operator=(const FECascadeData& Other); + ~FECascadeData(); + + float Size; + glm::mat4 ProjectionMat; + glm::mat4 ViewMat; + FEFramebuffer* FrameBuffer = nullptr; + float** Frustum = nullptr; + }; + + struct FELightComponent + { + friend class FELightSystem; + friend class FERenderer; + + FELightComponent(); + FELightComponent(FE_LIGHT_TYPE Type); + FELightComponent(FELightComponent& Other) = default; + ~FELightComponent(); + + FE_LIGHT_TYPE GetType(); glm::vec3 GetColor(); void SetColor(glm::vec3 NewValue); @@ -47,7 +71,8 @@ namespace FocalEngine float GetIntensity(); void SetIntensity(float NewValue); - FETransformComponent Transform; + float GetRange(); + void SetRange(float NewValue); bool IsLightEnabled(); void SetLightEnabled(bool NewValue); @@ -66,44 +91,6 @@ namespace FocalEngine bool IsCastShadows(); void SetCastShadows(bool NewValue); - protected: - glm::vec3 Color = glm::vec3(1.0f); - float Intensity = 1.0f; - - bool bEnabled = true; - - bool bStaticShadowBias = false; - float ShadowBias = 0.001f; - float ShadowBiasVariableIntensity = 1.0f; - bool bCastShadows = true; - float ShadowBlurFactor = 1.0f; - }; - - struct FECascadeData - { - friend FEScene; - friend FERenderer; - public: - FECascadeData(); - ~FECascadeData(); - - float Size; - glm::mat4 ProjectionMat; - glm::mat4 ViewMat; - FEFramebuffer* FrameBuffer = nullptr; - float** Frustum; - }; - - class FEDirectionalLight : public FELight - { - friend FEScene; - friend FERenderer; - public: - FEDirectionalLight(); - ~FEDirectionalLight(); - - glm::vec3 GetDirection(); - void SetDirection(glm::vec3 NewValue); int GetActiveCascades(); void SetActiveCascades(int NewValue); @@ -117,61 +104,39 @@ namespace FocalEngine float GetCSMXYDepth(); void SetCSMXYDepth(float NewValue); - void SetCastShadows(bool NewValue); - protected: - glm::vec3 Direction = glm::vec3(0.0f, 0.0f, -1.0f); - glm::vec3 DefaultDirection = glm::vec3(0.0f, 0.0f, -1.0f); - - bool bUseCascadeShadows = true; - int ActiveCascades = 4; - float ShadowCoverage = 50.0f; - float CSMZDepth = 3.0f; - float CSMXYDepth = 1.0f; - FECascadeData CascadeData[4]; - - void UpdateCascades(float CameraFov, float AspectRatio, float NearPlane, float FarPlane, glm::mat4 ViewMatrix, glm::vec3 CameraForward, glm::vec3 CameraRight, glm::vec3 CameraUp); - }; - - class FESpotLight : public FELight - { - friend FEScene; - friend FERenderer; - public: - FESpotLight(); - ~FESpotLight(); - - float GetRange(); - void SetRange(float NewValue); - float GetSpotAngle(); void SetSpotAngle(float NewValue); float GetSpotAngleOuter(); void SetSpotAngleOuter(float NewValue); + private: + FE_LIGHT_TYPE Type = FE_NULL_LIGHT; + + glm::vec3 Color = glm::vec3(1.0f); + float Intensity = 1.0f; - glm::vec3 GetDirection(); - void SetDirection(glm::vec3 NewValue); + bool bEnabled = true; - protected: + bool bStaticShadowBias = false; + float ShadowBias = 0.001f; + float ShadowBiasVariableIntensity = 1.0f; + bool bCastShadows = true; + float ShadowBlurFactor = 1.0f; + float Range = 10.0f; + + // Directional Light Specific glm::vec3 Direction = glm::vec3(0.0f, 0.0f, -1.0f); glm::vec3 DefaultDirection = glm::vec3(0.0f, 0.0f, -1.0f); + bool bUseCascadeShadows = true; + int ActiveCascades = 4; + float ShadowCoverage = 50.0f; + float CSMZDepth = 3.0f; + float CSMXYDepth = 1.0f; + FECascadeData CascadeData[4]; + + // Spotlight Specific float SpotAngle = 30.0f; float SpotAngleOuter = 45.0f; - float Range = 10; - }; - - class FEPointLight : public FELight - { - friend FEScene; - friend FERenderer; - public: - FEPointLight(); - ~FEPointLight(); - - float GetRange(); - void SetRange(float NewValue); - protected: - float Range = 10; }; } \ No newline at end of file diff --git a/SubSystems/Scene/Components/FEPrefabInstanceComponent.cpp b/SubSystems/Scene/Components/FEPrefabInstanceComponent.cpp new file mode 100644 index 0000000..8207bff --- /dev/null +++ b/SubSystems/Scene/Components/FEPrefabInstanceComponent.cpp @@ -0,0 +1,22 @@ +#include "FEPrefabInstanceComponent.h" +using namespace FocalEngine; + +FEPrefabInstanceComponent::FEPrefabInstanceComponent(FEPrefab* PrefabToSet) +{ + Prefab = PrefabToSet; +} + +FEPrefabInstanceComponent::FEPrefabInstanceComponent(const FEPrefabInstanceComponent& Other) +{ + Prefab = Other.Prefab; +} + +void FEPrefabInstanceComponent::operator=(const FEPrefabInstanceComponent& Other) +{ + Prefab = Other.Prefab; +} + +FEPrefab* FEPrefabInstanceComponent::GetPrefab() +{ + return Prefab; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FEPrefabInstanceComponent.h b/SubSystems/Scene/Components/FEPrefabInstanceComponent.h new file mode 100644 index 0000000..52657d7 --- /dev/null +++ b/SubSystems/Scene/Components/FEPrefabInstanceComponent.h @@ -0,0 +1,19 @@ +#pragma once +#include "../FEPrefab.h" + +namespace FocalEngine +{ + struct FEPrefabInstanceComponent + { + friend class FEPrefabInstanceSystem; + + FEPrefabInstanceComponent() {}; + FEPrefabInstanceComponent(FEPrefab* PrefabToSet); + FEPrefabInstanceComponent(const FEPrefabInstanceComponent& Other); + void operator=(const FEPrefabInstanceComponent& Other); + + FEPrefab* GetPrefab(); + private: + FEPrefab* Prefab = nullptr; + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FESkyDomeComponent.cpp b/SubSystems/Scene/Components/FESkyDomeComponent.cpp new file mode 100644 index 0000000..10a74af --- /dev/null +++ b/SubSystems/Scene/Components/FESkyDomeComponent.cpp @@ -0,0 +1,7 @@ +#include "FESkyDomeComponent.h" +using namespace FocalEngine; + +FESkyDomeComponent::FESkyDomeComponent() +{ + +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FESkyDomeComponent.h b/SubSystems/Scene/Components/FESkyDomeComponent.h new file mode 100644 index 0000000..9e5e4d7 --- /dev/null +++ b/SubSystems/Scene/Components/FESkyDomeComponent.h @@ -0,0 +1,13 @@ +#pragma once +#include "../Renderer/FEGameModel.h" + +namespace FocalEngine +{ + struct FESkyDomeComponent + { + FESkyDomeComponent(); + FESkyDomeComponent(FESkyDomeComponent& Other) = default; + + int DummyVariable; + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FETagComponent.cpp b/SubSystems/Scene/Components/FETagComponent.cpp new file mode 100644 index 0000000..9eb1761 --- /dev/null +++ b/SubSystems/Scene/Components/FETagComponent.cpp @@ -0,0 +1,17 @@ +#include "FETagComponent.h" +using namespace FocalEngine; + +FETagComponent::FETagComponent(std::string TagToSet) +{ + Tag = TagToSet; +} + +std::string FETagComponent::GetTag() const +{ + return Tag; +} + +void FETagComponent::SetTag(std::string NewTag) +{ + Tag = NewTag; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FETagComponent.h b/SubSystems/Scene/Components/FETagComponent.h new file mode 100644 index 0000000..6d9aacc --- /dev/null +++ b/SubSystems/Scene/Components/FETagComponent.h @@ -0,0 +1,16 @@ +#pragma once +#include "../Core/FEObject.h" + +namespace FocalEngine +{ + struct FETagComponent + { + FETagComponent() {}; + FETagComponent(std::string TagToSet); + + std::string GetTag() const; + void SetTag(std::string NewTag); + private: + std::string Tag = ""; + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FETerrainComponent.cpp b/SubSystems/Scene/Components/FETerrainComponent.cpp new file mode 100644 index 0000000..7cc5436 --- /dev/null +++ b/SubSystems/Scene/Components/FETerrainComponent.cpp @@ -0,0 +1,342 @@ +#include "FETerrainComponent.h" +using namespace FocalEngine; + +FETerrainLayer::FETerrainLayer(const std::string Name) {} +FEMaterial* FETerrainLayer::GetMaterial() +{ + return Material; +} + +void FETerrainLayer::SetMaterial(FEMaterial* NewValue) +{ + if (NewValue->IsCompackPacking()) + Material = NewValue; +} + +FETerrainComponent::FETerrainComponent() +{ + Layers.resize(FE_TERRAIN_MAX_LAYERS); + for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + { + Layers[i] = nullptr; + } + + LayerMaps.resize(FE_TERRAIN_MAX_LAYERS / FE_TERRAIN_LAYER_PER_TEXTURE); + LayerMapsRawData.resize(FE_TERRAIN_MAX_LAYERS / FE_TERRAIN_LAYER_PER_TEXTURE); + LayerMaps[0] = nullptr; + LayerMaps[1] = nullptr; + + FE_GL_ERROR(glGenBuffers(1, &GPULayersDataBuffer)); + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, GPULayersDataBuffer)); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(float) * FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * FE_TERRAIN_MAX_LAYERS, nullptr, GL_DYNAMIC_DRAW)); + + GPULayersData.resize(FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * FE_TERRAIN_MAX_LAYERS); + OldGPULayersData.resize(FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * FE_TERRAIN_MAX_LAYERS); +} + +FETerrainComponent::~FETerrainComponent() {} + +bool FETerrainComponent::IsVisible() +{ + return bVisible; +} + +void FETerrainComponent::SetVisibility(const bool NewValue) +{ + bVisible = NewValue; +} + +bool FETerrainComponent::IsCastingShadows() +{ + return bCastShadows; +} + +void FETerrainComponent::SetCastingShadows(const bool NewValue) +{ + bCastShadows = NewValue; +} + +bool FETerrainComponent::IsReceivingShadows() +{ + return bReceiveShadows; +} + +void FETerrainComponent::SetReceivingShadows(const bool NewValue) +{ + bReceiveShadows = NewValue; +} + +void FETerrainComponent::SetWireframeMode(const bool NewValue) +{ + bWireframeMode = NewValue; +} + +bool FETerrainComponent::IsWireframeMode() +{ + return bWireframeMode; +} + +float FETerrainComponent::GetHightScale() +{ + return HightScale; +} + +FETexture* FETerrainComponent::GetHeightMap() const +{ + return HeightMap; +} + +void FETerrainComponent::SetHightScale(const float NewValue) +{ + if (NewValue <= 0) + return; + + if (HightScale != NewValue) + bDirtyFlag = true; + + HightScale = NewValue; +} + +glm::vec2 FETerrainComponent::GetTileMult() +{ + return TileMult; +} + +void FETerrainComponent::SetTileMult(const glm::vec2 NewValue) +{ + TileMult = NewValue; +} + +float FETerrainComponent::GetLODLevel() +{ + return LODLevel; +} + +void FETerrainComponent::SetLODLevel(float NewValue) +{ + if (NewValue < 2.0) + NewValue = 2.0; + + if (NewValue > 128.0) + NewValue = 128.0; + + LODLevel = NewValue; +} + +float FETerrainComponent::GetChunkPerSide() +{ + return ChunkPerSide; +} + +void FETerrainComponent::SetChunkPerSide(float NewValue) +{ + if (NewValue < 1.0f) + NewValue = 1.0f; + + if (NewValue > 16.0f) + NewValue = 16.0f; + + if (ChunkPerSide != NewValue) + bDirtyFlag = true; + + ChunkPerSide = NewValue; +} + +float FETerrainComponent::GetDisplacementScale() +{ + return DisplacementScale; +} + +void FETerrainComponent::SetDisplacementScale(const float NewValue) +{ + DisplacementScale = NewValue; +} + +float FETerrainComponent::GetXSize() +{ + return XSize; +} + +float FETerrainComponent::GetZSize() +{ + return ZSize; +} + +bool FETerrainComponent::GetNextEmptyLayerSlot(size_t& NextEmptyLayerIndex) +{ + for (size_t i = 0; i < Layers.size(); i++) + { + if (Layers[i] == nullptr) + { + NextEmptyLayerIndex = i; + return true; + } + } + + return false; +} + +FETerrainLayer* FETerrainComponent::ActivateVacantLayerSlot(FEMaterial* Material) +{ + size_t LayerIndex = 0; + if (!GetNextEmptyLayerSlot(LayerIndex)) + { + LOG.Add("FETerrainComponent::activateLayerSlot was not able to acquire vacant layer index", "FE_LOG_RENDERING", FE_LOG_WARNING); + return nullptr; + } + + if (Material == nullptr) + { + LOG.Add("FETerrainComponent::activateLayerSlot material is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); + return nullptr; + } + + if (!Material->IsCompackPacking()) + { + LOG.Add("FETerrainComponent::activateLayerSlot material is not compactly packed", "FE_LOG_RENDERING", FE_LOG_WARNING); + return nullptr; + } + + if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + { + LOG.Add("FETerrainComponent::activateLayerSlot with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); + return nullptr; + } + + if (Layers[LayerIndex] != nullptr) + { + LOG.Add("FETerrainComponent::activateLayerSlot on indicated layer slot layer is already active", "FE_LOG_RENDERING", FE_LOG_WARNING); + return nullptr; + } + + Layers[LayerIndex] = new FETerrainLayer(std::string("Layer_") + std::to_string(LayerIndex)); + Layers[LayerIndex]->Material = Material; + + if (LayerIndex == 0) + SetWireframeMode(false); + + return Layers[LayerIndex]; +} + +FETerrainLayer* FETerrainComponent::GetLayerInSlot(const size_t LayerIndex) +{ + if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + { + LOG.Add("FETerrainComponent::getLayerInSlot with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); + return nullptr; + } + + return Layers[LayerIndex]; +} + +void FETerrainComponent::DeleteLayerInSlot(const size_t LayerIndex) +{ + if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + { + LOG.Add("FETerrainComponent::deleteLayerInSlot with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + if (Layers[LayerIndex] == nullptr) + { + LOG.Add("FETerrainComponent::deleteLayerInSlot on indicated layer slot layer is already inactive", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + delete Layers[LayerIndex]; + Layers[LayerIndex] = nullptr; + + if (LayerIndex == 0 && Layers[LayerIndex + 1] == nullptr) + SetWireframeMode(true); + + if (LayerIndex + 1 >= FE_TERRAIN_MAX_LAYERS) + return; + + size_t CurrentIndex = LayerIndex + 1; + while (true) + { + if (CurrentIndex >= FE_TERRAIN_MAX_LAYERS || Layers[CurrentIndex] == nullptr) + return; + + Layers[CurrentIndex - 1] = Layers[CurrentIndex]; + Layers[CurrentIndex] = nullptr; + + CurrentIndex++; + } +} + +void FETerrainComponent::LoadLayersDataToGPU() +{ + bool GPUDataIsStale = false; + for (size_t i = 0; i < Layers.size(); i++) + { + const size_t index = i * FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER; + if (Layers[i] != nullptr && Layers[i]->GetMaterial()->IsCompackPacking()) + { + const FEMaterial* CurrentMaterial = Layers[i]->GetMaterial(); + // normalMapIntensity + GPULayersData[index] = CurrentMaterial->GetNormalMapIntensity(); + // AOIntensity + GPULayersData[index + 1] = CurrentMaterial->GetAmbientOcclusionIntensity(); + // AOMapIntensity + GPULayersData[index + 2] = CurrentMaterial->GetAmbientOcclusionMapIntensity(); + // roughness + GPULayersData[index + 3] = CurrentMaterial->GetRoughness(); + // roughnessMapIntensity + GPULayersData[index + 4] = CurrentMaterial->GetRoughnessMapIntensity(); + // metalness + GPULayersData[index + 5] = CurrentMaterial->GetMetalness(); + // metalnessMapIntensity + GPULayersData[index + 6] = CurrentMaterial->GetMetalnessMapIntensity(); + // displacementMapIntensity + GPULayersData[index + 7] = CurrentMaterial->GetDisplacementMapIntensity(); + // tiling + GPULayersData[index + 8] = CurrentMaterial->GetTiling(); + } + else + { + for (size_t j = 0; j < FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER; j++) + { + GPULayersData[index + j] = -1.0f; + } + } + } + + for (size_t i = 0; i < FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * FE_TERRAIN_MAX_LAYERS; i++) + { + if (GPULayersData[i] != OldGPULayersData[i]) + { + GPUDataIsStale = true; + OldGPULayersData = GPULayersData; + break; + } + } + + if (GPUDataIsStale) + { + float* TerrainLayersDataPtr = static_cast(glMapNamedBufferRange(GPULayersDataBuffer, 0, + sizeof(float) * FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * + FE_TERRAIN_MAX_LAYERS, + GL_MAP_WRITE_BIT/* | + GL_MAP_INVALIDATE_BUFFER_BIT | + GL_MAP_UNSYNCHRONIZED_BIT*/)); + for (size_t i = 0; i < FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER * FE_TERRAIN_MAX_LAYERS; i++) + { + TerrainLayersDataPtr[i] = GPULayersData[i]; + } + FE_GL_ERROR(glUnmapNamedBuffer(GPULayersDataBuffer)); + } +} + +int FETerrainComponent::LayersUsed() +{ + int LayersUsed = 0; + for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + { + if (GetLayerInSlot(i) == nullptr) + break; + LayersUsed++; + } + + return LayersUsed; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FETerrainComponent.h b/SubSystems/Scene/Components/FETerrainComponent.h new file mode 100644 index 0000000..0068688 --- /dev/null +++ b/SubSystems/Scene/Components/FETerrainComponent.h @@ -0,0 +1,135 @@ +#pragma once + +#include "../Core/FEGeometricTools.h" +#include "../Renderer/FEMaterial.h" + +namespace FocalEngine +{ +#define FE_TERRAIN_MAX_LAYERS 8 +#define FE_TERRAIN_MATERIAL_PROPERTIES_PER_LAYER 9 +#define FE_TERRAIN_LAYER_PER_TEXTURE 4 +#define FE_TERRAIN_STANDARD_HIGHT_MAP_RESOLUTION 1024 +#define FE_TERRAIN_STANDARD_LAYER_MAP_RESOLUTION 512 + + class FETerrainLayer + { + friend class FETerrainComponent; + friend class FETerrainSystem; + + std::string Name; + FEMaterial* Material = nullptr; + FETerrainLayer(std::string Name); + public: + void SetMaterial(FEMaterial* NewValue); + FEMaterial* GetMaterial(); + + std::string GetName() { return Name; } + void SetName(std::string NewName) { Name = NewName; } + }; + + enum FE_TERRAIN_BRUSH_MODE + { + FE_TERRAIN_BRUSH_NONE = 0, + FE_TERRAIN_BRUSH_SCULPT_DRAW = 1, + FE_TERRAIN_BRUSH_SCULPT_DRAW_INVERSED = 2, + FE_TERRAIN_BRUSH_SCULPT_LEVEL = 3, + FE_TERRAIN_BRUSH_SCULPT_SMOOTH = 4, + FE_TERRAIN_BRUSH_LAYER_DRAW = 5 + }; + + // Ideally, this should not be here + class FEEntity; + + class FETerrainComponent + { + friend class FERenderer; + friend class FEResourceManager; + friend class FETerrainSystem; + public: + FETerrainComponent(); + FETerrainComponent(const FETerrainComponent& Other) = default; + ~FETerrainComponent(); + + bool IsVisible(); + void SetVisibility(bool NewValue); + + bool IsCastingShadows(); + void SetCastingShadows(bool NewValue); + + bool IsReceivingShadows(); + void SetReceivingShadows(bool NewValue); + + FEShader* Shader = nullptr; + FETexture* GetHeightMap() const; + + std::vector LayerMaps; + FETexture* ProjectedMap = nullptr; + + bool IsWireframeMode(); + void SetWireframeMode(bool NewValue); + + float GetHightScale(); + void SetHightScale(float NewValue); + + float GetDisplacementScale(); + void SetDisplacementScale(float NewValue); + + glm::vec2 GetTileMult(); + void SetTileMult(glm::vec2 NewValue); + + float GetLODLevel(); + void SetLODLevel(float NewValue); + + float GetChunkPerSide(); + void SetChunkPerSide(float NewValue); + + float GetXSize(); + float GetZSize(); + + bool GetNextEmptyLayerSlot(size_t& NextEmptyLayerIndex); + FETerrainLayer* GetLayerInSlot(size_t LayerIndex); + + int LayersUsed(); + private: + + FETexture* HeightMap = nullptr; + + bool bWireframeMode = false; + bool bVisible = true; + bool bCastShadows = true; + bool bReceiveShadows = true; + bool bDirtyFlag = false; + + float HightScale = 1.0f; + float DisplacementScale = 0.2f; + float ScaleFactor = 1.0f; + glm::vec2 TileMult = glm::vec2(1.0); + glm::vec2 HightMapShift = glm::vec2(0.0); + float ChunkPerSide = 2.0f; + + float LODLevel = 64.0f; + FEAABB AABB; + FEAABB FinalAABB; + float XSize = 0.0f; + float ZSize = 0.0f; + + std::vector HeightMapArray; + + // **************************** TERRAIN EDITOR TOOLS **************************** + FEFramebuffer* BrushOutputFB = nullptr; + FEFramebuffer* BrushVisualFB = nullptr; + // **************************** TERRAIN EDITOR TOOLS END ************************ + + std::vector SnapedInstancedEntities; + + std::vector Layers; + std::vector LayerMapsRawData; + FETerrainLayer* ActivateVacantLayerSlot(FEMaterial* Material); + void DeleteLayerInSlot(size_t LayerIndex); + + GLuint GPULayersDataBuffer = 0; + void LoadLayersDataToGPU(); + std::vector GPULayersData; + std::vector OldGPULayersData; + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FETransformComponent.cpp b/SubSystems/Scene/Components/FETransformComponent.cpp new file mode 100644 index 0000000..abbc3ae --- /dev/null +++ b/SubSystems/Scene/Components/FETransformComponent.cpp @@ -0,0 +1,427 @@ +#include "FETransformComponent.h" +#include "../FEScene.h" +using namespace FocalEngine; + +FETransformComponent::FETransformComponent() +{ + Position = glm::vec3(0.0f); + RotationAngles = glm::vec3(0.0f); + RotationQuaternion = glm::quat(1.0f, glm::vec3(0.0f)); + Scale = glm::vec3(1.0f, 1.0f, 1.0f); + + Update(); +} + +FETransformComponent::FETransformComponent(glm::mat4 Matrix) +{ + Position = glm::vec3(Matrix[3][0], Matrix[3][1], Matrix[3][2]); + Scale = glm::vec3(glm::length(Matrix[0]), glm::length(Matrix[1]), glm::length(Matrix[2])); + + Matrix[3][0] = 0.0f; + Matrix[3][1] = 0.0f; + Matrix[3][2] = 0.0f; + + Matrix[0] /= Scale.x; + Matrix[1] /= Scale.y; + Matrix[2] /= Scale.z; + + RotationQuaternion = glm::quat_cast(Matrix); + RotationAngles = glm::eulerAngles(RotationQuaternion) * 180.0f / glm::pi(); + + Update(); +} + +void FETransformComponent::CopyFrom(const FETransformComponent& Other) +{ + ParentEntity = Other.ParentEntity; + + bDirtyFlag = Other.bDirtyFlag; + bSceneIndependent = Other.bSceneIndependent; + + WorldSpaceMatrix = Other.WorldSpaceMatrix; + LocalSpaceMatrix = Other.LocalSpaceMatrix; + + Position = Other.Position; + + RotationQuaternion = Other.RotationQuaternion; + RotationAngles = Other.RotationAngles; + + bUniformScaling = Other.bUniformScaling; + Scale = Other.Scale; +} + +FETransformComponent::FETransformComponent(const FETransformComponent& Other) +{ + if (this == &Other) + return; + + CopyFrom(Other); +} + +FETransformComponent& FETransformComponent::operator=(const FETransformComponent& Other) +{ + if (this == &Other) + return *this; + + CopyFrom(Other); + + return *this; +} + +FETransformComponent::~FETransformComponent() +{ +} + +glm::vec3 FETransformComponent::GetPosition(FE_COORDIANTE_SPACE_TYPE SpaceType) const +{ + if (SpaceType == FE_WORLD_SPACE) + { + glm::dvec3 DoubleScale; + glm::dquat DoubleRotationQuaternion; + glm::dvec3 DoubleTranslation; + if (GEOMETRY.DecomposeMatrixToTranslationRotationScale(WorldSpaceMatrix, DoubleTranslation, DoubleRotationQuaternion, DoubleScale)) + { + return DoubleTranslation; + } + } + + return Position; +} + +glm::vec3 FETransformComponent::GetRotation(FE_COORDIANTE_SPACE_TYPE SpaceType) const +{ + if (SpaceType == FE_WORLD_SPACE) + { + glm::dvec3 DoubleScale; + glm::dquat DoubleRotationQuaternion; + glm::dvec3 DoubleTranslation; + if (GEOMETRY.DecomposeMatrixToTranslationRotationScale(WorldSpaceMatrix, DoubleTranslation, DoubleRotationQuaternion, DoubleScale)) + { + glm::vec3 RotationAnglesInRadians = glm::eulerAngles(DoubleRotationQuaternion); + glm::vec3 RotationAnglesInDegrees = RotationAnglesInRadians * RADIANS_TOANGLE_COF; + + return RotationAnglesInDegrees; + } + } + + return RotationAngles; +} + +glm::quat FETransformComponent::GetQuaternion(FE_COORDIANTE_SPACE_TYPE SpaceType) const +{ + if (SpaceType == FE_WORLD_SPACE) + { + glm::dvec3 DoubleScale; + glm::dquat DoubleRotationQuaternion; + glm::dvec3 DoubleTranslation; + if (GEOMETRY.DecomposeMatrixToTranslationRotationScale(WorldSpaceMatrix, DoubleTranslation, DoubleRotationQuaternion, DoubleScale)) + { + return DoubleRotationQuaternion; + } + } + + return RotationQuaternion; +} + +glm::vec3 FETransformComponent::GetScale(FE_COORDIANTE_SPACE_TYPE SpaceType) const +{ + if (SpaceType == FE_WORLD_SPACE) + { + glm::dvec3 DoubleScale; + glm::dquat DoubleRotationQuaternion; + glm::dvec3 DoubleTranslation; + if (GEOMETRY.DecomposeMatrixToTranslationRotationScale(WorldSpaceMatrix, DoubleTranslation, DoubleRotationQuaternion, DoubleScale)) + { + return DoubleScale; + } + } + + return Scale; +} + +glm::mat4 FETransformComponent::GetParentMatrix() const +{ + if (bSceneIndependent) + return glm::identity(); + + // We assume that WorldSpaceMatrix was set in the previous frame like this: + // WorldSpaceMatrix = ParentWorldSpace * LocalSpaceMatrix; + // Therefore, we can use it to extract ParentWorldSpace. + glm::mat4 ParentWorldSpace = WorldSpaceMatrix * glm::inverse(LocalSpaceMatrix); + return ParentWorldSpace; +} + +void FETransformComponent::MoveAlongAxis(const glm::vec3& Axis, float MovementValue, FE_COORDIANTE_SPACE_TYPE SpaceType) +{ + glm::vec3 NewPosition = GetPosition(); + glm::vec3 LocalAlternativeAxis = Axis * MovementValue; + + if (SpaceType == FE_WORLD_SPACE) + LocalAlternativeAxis = glm::inverse(GetParentMatrix()) * glm::vec4(LocalAlternativeAxis, 0.0f); + + NewPosition += LocalAlternativeAxis; + SetPosition(NewPosition); +} + +void FETransformComponent::SetPosition(const glm::vec3 NewPosition, FE_COORDIANTE_SPACE_TYPE SpaceType) +{ + if (SpaceType == FE_LOCAL_SPACE) + { + Position = NewPosition; + Update(); + } + + if (SpaceType == FE_WORLD_SPACE) + { + glm::vec3 CurrentWorldPosition = GetPosition(FE_WORLD_SPACE); + glm::vec3 Difference = NewPosition - CurrentWorldPosition; + + MoveAlongAxis(glm::vec3(1, 0, 0), Difference.x, FE_WORLD_SPACE); + MoveAlongAxis(glm::vec3(0, 1, 0), Difference.y, FE_WORLD_SPACE); + MoveAlongAxis(glm::vec3(0, 0, 1), Difference.z, FE_WORLD_SPACE); + } + + SetDirtyFlag(true); +} + +void FETransformComponent::RotateQuaternion(const float Angle, const glm::vec3 Axis) +{ + RotationQuaternion = glm::quat(cos(Angle / 2), + Axis.x * sin(Angle / 2), + Axis.y * sin(Angle / 2), + Axis.z * sin(Angle / 2)) * RotationQuaternion; +} + +void FETransformComponent::RotateAroundAxis(const glm::vec3& Axis, const float& RotationAmount, FE_COORDIANTE_SPACE_TYPE SpaceType) +{ + glm::vec3 FinalAxis = Axis; + + if (SpaceType == FE_WORLD_SPACE) + { + glm::dvec3 DoubleScale; + glm::dquat DoubleRotation; + glm::dvec3 DoubleTranslation; + glm::dvec3 DoubleSkew; + glm::dvec4 DoublePerspective; + glm::dmat4 ParentMatrix = GetParentMatrix(); + bool Success = glm::decompose(ParentMatrix, DoubleScale, DoubleRotation, DoubleTranslation, DoubleSkew, DoublePerspective); + if (Success) + { + FinalAxis = glm::inverse(glm::quat(DoubleRotation)) * glm::vec3(Axis); + } + } + + glm::quat LocalRotationQuaternion = glm::quat(cos(RotationAmount * ANGLE_TORADIANS_COF / 2), + FinalAxis.x * sin(RotationAmount * ANGLE_TORADIANS_COF / 2), + FinalAxis.y * sin(RotationAmount * ANGLE_TORADIANS_COF / 2), + FinalAxis.z * sin(RotationAmount * ANGLE_TORADIANS_COF / 2)); + + RotationQuaternion = LocalRotationQuaternion * RotationQuaternion; + Update(); +} + +void FETransformComponent::SetRotation(const glm::vec3 NewRotation, FE_COORDIANTE_SPACE_TYPE SpaceType) +{ + if (RotationAngles == NewRotation) + return; + + if (SpaceType == FE_LOCAL_SPACE) + { + RotationQuaternion = glm::quat(1.0f, glm::vec3(0.0f)); + RotateQuaternion(static_cast(NewRotation.x) * ANGLE_TORADIANS_COF, glm::vec3(1, 0, 0)); + RotateQuaternion(static_cast(NewRotation.y) * ANGLE_TORADIANS_COF, glm::vec3(0, 1, 0)); + RotateQuaternion(static_cast(NewRotation.z) * ANGLE_TORADIANS_COF, glm::vec3(0, 0, 1)); + + RotationAngles = NewRotation; + Update(); + } + else if (SpaceType == FE_WORLD_SPACE) + { + glm::vec3 CurrentWorldRotation = GetRotation(FE_WORLD_SPACE); + glm::vec3 Difference = NewRotation - CurrentWorldRotation; + RotateAroundAxis(glm::vec3(1, 0, 0), Difference.x, FE_WORLD_SPACE); + RotateAroundAxis(glm::vec3(0, 1, 0), Difference.y, FE_WORLD_SPACE); + RotateAroundAxis(glm::vec3(0, 0, 1), Difference.z, FE_WORLD_SPACE); + } + + SetDirtyFlag(true); +} + +void FETransformComponent::SetQuaternion(glm::quat Quaternion, FE_COORDIANTE_SPACE_TYPE SpaceType) +{ + glm::mat4 ParentMatrix; + if (SpaceType == FE_WORLD_SPACE) + { + // It is important to get ParentMatrix before Update(). + ParentMatrix = GetParentMatrix(); + } + + RotationQuaternion = Quaternion; + glm::vec3 NewRotationAngle = glm::eulerAngles(Quaternion); + + NewRotationAngle.x /= ANGLE_TORADIANS_COF; + NewRotationAngle.y /= ANGLE_TORADIANS_COF; + NewRotationAngle.z /= ANGLE_TORADIANS_COF; + + RotationAngles = NewRotationAngle; + Update(); + + if (SpaceType == FE_WORLD_SPACE) + { + // If we did not have scene hierarchy, we can just set LocalSpaceMatrix + // But since we have it, we need to calculate LocalSpaceMatrix to sustain the new world rotation. + LocalSpaceMatrix = glm::inverse(ParentMatrix) * LocalSpaceMatrix; + + // Now we will extract new local rotation. + glm::dvec3 DoubleScale; + glm::dquat DoubleRotationQuaternion; + glm::dvec3 DoubleTranslation; + if (GEOMETRY.DecomposeMatrixToTranslationRotationScale(LocalSpaceMatrix, DoubleTranslation, DoubleRotationQuaternion, DoubleScale)) + { + RotationQuaternion = DoubleRotationQuaternion; + RotationAngles = glm::eulerAngles(RotationQuaternion) * RADIANS_TOANGLE_COF; + } + } + + SetDirtyFlag(true); +} + +void FETransformComponent::RotateByQuaternion(const glm::quat Quaternion) +{ + RotationQuaternion = Quaternion * RotationQuaternion; + glm::vec3 NewRotationAngle = glm::eulerAngles(GetQuaternion()); + + NewRotationAngle.x /= ANGLE_TORADIANS_COF; + NewRotationAngle.y /= ANGLE_TORADIANS_COF; + NewRotationAngle.z /= ANGLE_TORADIANS_COF; + + RotationAngles = NewRotationAngle; + Update(); + + SetDirtyFlag(true); +} + +void FETransformComponent::SetScale(const glm::vec3 NewScale, FE_COORDIANTE_SPACE_TYPE SpaceType) +{ + glm::mat4 ParentMatrix; + if (SpaceType == FE_WORLD_SPACE) + { + // It is important to get ParentMatrix before Update(). + ParentMatrix = GetParentMatrix(); + } + + Scale = NewScale; + Update(); + + if (SpaceType == FE_WORLD_SPACE) + { + // If we did not have a scene hierarchy, we could just set LocalSpaceMatrix. + // But since we have it, we need to calculate LocalSpaceMatrix to sustain the new world scale. + LocalSpaceMatrix = glm::inverse(ParentMatrix) * LocalSpaceMatrix; + + // Now we will extract new local Scale. + glm::dvec3 DoubleScale; + glm::dquat DoubleRotationQuaternion; + glm::dvec3 DoubleTranslation; + if (GEOMETRY.DecomposeMatrixToTranslationRotationScale(LocalSpaceMatrix, DoubleTranslation, DoubleRotationQuaternion, DoubleScale)) + { + Scale = DoubleScale; + } + } + + SetDirtyFlag(true); +} + +void FETransformComponent::Update() +{ + LocalSpaceMatrix = glm::identity(); + LocalSpaceMatrix = glm::translate(LocalSpaceMatrix, Position); + LocalSpaceMatrix *= glm::toMat4(RotationQuaternion); + LocalSpaceMatrix = glm::scale(LocalSpaceMatrix, glm::vec3(Scale[0], Scale[1], Scale[2])); +} + +glm::mat4 FETransformComponent::GetWorldMatrix() +{ + if (bSceneIndependent) + return LocalSpaceMatrix; + + if (ParentEntity != nullptr) + { + FEScene* ParentScene = ParentEntity->GetParentScene(); + FENaiveSceneGraphNode* ParentNode = ParentScene->SceneGraph.GetNodeByEntityID(ParentEntity->GetObjectID()); + if (ParentNode != nullptr) + { + // If this is root node or parent is root node, then world space matrix is equal to local space matrix. + if (ParentNode->GetParent() == nullptr || ParentNode->GetParent() == ParentScene->SceneGraph.GetRoot()) + { + WorldSpaceMatrix = LocalSpaceMatrix; + } + } + else + { + LOG.Add("Parent entity is not in the scene graph!", "FE_LOG_ECS", FE_LOG_ERROR); + } + } + else + { + LOG.Add("Parent entity is not set for transform component!", "FE_LOG_ECS", FE_LOG_ERROR); + } + + return WorldSpaceMatrix; +} + +void FETransformComponent::ForceSetWorldMatrix(glm::mat4 NewMatrix) +{ + WorldSpaceMatrix = NewMatrix; +} + +glm::mat4 FETransformComponent::GetLocalMatrix() const +{ + return LocalSpaceMatrix; +} + +void FETransformComponent::ForceSetLocalMatrix(glm::mat4 NewMatrix) +{ + LocalSpaceMatrix = NewMatrix; +} + +bool FETransformComponent::IsDirty() const +{ + return bDirtyFlag; +} + +void FETransformComponent::SetDirtyFlag(const bool NewValue) +{ + bDirtyFlag = NewValue; +} + +FETransformComponent FETransformComponent::Combine(const FETransformComponent& Other) const +{ + FETransformComponent Result; + + Result.SetPosition(GetPosition() + Other.GetPosition()); + Result.SetRotation(GetRotation() + Other.GetRotation()); + Result.SetScale(GetScale() * Other.GetScale()); + + return Result; +} + +bool FETransformComponent::IsUniformScalingSet() const +{ + return bUniformScaling; +} + +void FETransformComponent::SetUniformScaling(const bool NewValue) +{ + bUniformScaling = NewValue; +} + +bool FETransformComponent::IsSceneIndependent() const +{ + return bSceneIndependent; +} + +void FETransformComponent::SetSceneIndependent(bool NewValue) +{ + bSceneIndependent = NewValue; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FETransformComponent.h b/SubSystems/Scene/Components/FETransformComponent.h new file mode 100644 index 0000000..1a96699 --- /dev/null +++ b/SubSystems/Scene/Components/FETransformComponent.h @@ -0,0 +1,83 @@ +#pragma once + +#include "../Core/FEObject.h" +#include "../Core/FEGeometricTools.h" + +namespace FocalEngine +{ + enum FE_COORDIANTE_SPACE_TYPE + { + FE_LOCAL_SPACE = 0, + FE_WORLD_SPACE = 1 + }; + + class FETransformComponent + { + friend class FEScene; + friend class FERenderer; + friend class FETransformSystem; + public: + FETransformComponent(); + FETransformComponent(glm::mat4 Matrix); + ~FETransformComponent(); + + FETransformComponent Combine(const FETransformComponent& Other) const; + + glm::vec3 GetPosition(FE_COORDIANTE_SPACE_TYPE SpaceType = FE_LOCAL_SPACE) const; + glm::vec3 GetRotation(FE_COORDIANTE_SPACE_TYPE SpaceType = FE_LOCAL_SPACE) const; + glm::quat GetQuaternion(FE_COORDIANTE_SPACE_TYPE SpaceType = FE_LOCAL_SPACE) const; + glm::vec3 GetScale(FE_COORDIANTE_SPACE_TYPE SpaceType = FE_LOCAL_SPACE) const; + + void SetPosition(glm::vec3 NewPosition, FE_COORDIANTE_SPACE_TYPE SpaceType = FE_LOCAL_SPACE); + void SetRotation(glm::vec3 NewRotation, FE_COORDIANTE_SPACE_TYPE SpaceType = FE_LOCAL_SPACE); + void RotateAroundAxis(const glm::vec3& Axis, const float& RotationAmount, FE_COORDIANTE_SPACE_TYPE SpaceType = FE_LOCAL_SPACE); + void SetQuaternion(glm::quat Quaternion, FE_COORDIANTE_SPACE_TYPE SpaceType = FE_LOCAL_SPACE); + void RotateByQuaternion(glm::quat Quaternion); + void SetScale(glm::vec3 NewScale, FE_COORDIANTE_SPACE_TYPE SpaceType = FE_LOCAL_SPACE); + + glm::mat4 GetWorldMatrix(); + // Use this function only if you know what you are doing + // Usually, it is used to forcefully set the world matrix before the scene update occurs + void ForceSetWorldMatrix(glm::mat4 NewMatrix); + glm::mat4 GetLocalMatrix() const; + // Use this function only if you know what you are doing + void ForceSetLocalMatrix(glm::mat4 NewMatrix); + + void Update(); + + bool IsDirty() const; + void SetDirtyFlag(bool NewValue); + + bool IsUniformScalingSet() const; + void SetUniformScaling(bool NewValue); + + bool IsSceneIndependent() const; + void SetSceneIndependent(bool NewValue); + private: + FETransformComponent(const FETransformComponent& Other); + FETransformComponent& operator=(const FETransformComponent& Other); + + FEEntity* ParentEntity = nullptr; + + bool bDirtyFlag = false; + bool bSceneIndependent = false; + + glm::mat4 WorldSpaceMatrix = glm::identity(); + glm::mat4 LocalSpaceMatrix = glm::identity(); + + glm::vec3 Position = glm::vec3(0.0f); + + glm::quat RotationQuaternion = glm::quat(1.0f, glm::vec3(0.0f)); + glm::vec3 RotationAngles = glm::vec3(0.0f); + + bool bUniformScaling = true; + glm::vec3 Scale = glm::vec3(1.0f); + + void RotateQuaternion(float Angle, glm::vec3 Axis); + glm::mat4 GetParentMatrix() const; + + void MoveAlongAxis(const glm::vec3& Axis, float MovementValue, FE_COORDIANTE_SPACE_TYPE SpaceType = FE_LOCAL_SPACE); + + void CopyFrom(const FETransformComponent& Other); + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/FEVirtualUIComponent.cpp b/SubSystems/Scene/Components/FEVirtualUIComponent.cpp new file mode 100644 index 0000000..5781cf4 --- /dev/null +++ b/SubSystems/Scene/Components/FEVirtualUIComponent.cpp @@ -0,0 +1,546 @@ +#include "FEVirtualUIComponent.h" +#include "../ResourceManager/FEResourceManager.h" +#include "../FEEntity.h" +using namespace FocalEngine; + +FEVirtualUIComponent::FEVirtualUIComponent(int Width, int Height, FEMesh* CanvasMesh) +{ + if (Width == 0 || Height == 0) + { + LOG.Add("Invalid width or height in FEVirtualUIComponent::FEVirtualUIComponent", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } + + if (CanvasMesh == nullptr) + CanvasMesh = RESOURCE_MANAGER.GetMeshByName("FEPlane")[0]; + + SetCanvasMesh(CanvasMesh); + + Framebuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_COLOR_ATTACHMENT, Width, Height, false); + VirtualUI = APPLICATION.AddVirtualUI(Framebuffer->FBO, Width, Height); +} + +FEVirtualUIComponent::~FEVirtualUIComponent() +{ + delete Framebuffer; + APPLICATION.RemoveVirtualUI(VirtualUI); + + UnregisterCallbacksForWindow(); +} + +FEMesh* FEVirtualUIComponent::GetCanvasMesh() const +{ + return CanvasMesh; +} + +void FEVirtualUIComponent::SetCanvasMesh(FEMesh* NewCanvasMesh) +{ + if (NewCanvasMesh == nullptr) + { + LOG.Add("Invalid mesh in FEVirtualUIComponent::SetCanvasMesh", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } + + CanvasMesh = NewCanvasMesh; + OriginalMeshTrianglePositions = CanvasMesh->GetTrianglePositions(); + TransformedMeshTrianglePositions = OriginalMeshTrianglePositions; + MeshTriangleUVs = CanvasMesh->GetTriangleUVs(); +} + +glm::vec2 FEVirtualUIComponent::GetCanvasResolution() const +{ + glm::vec2 Result; + Result.x = static_cast(Framebuffer->GetWidth()); + Result.y = static_cast(Framebuffer->GetHeight()); + + return Result; +} + +void FEVirtualUIComponent::SetCanvasResolution(glm::vec2 NewResolution) +{ + if (NewResolution.x <= 0 || NewResolution.y <= 0) + { + LOG.Add("Invalid width or height in FEVirtualUIComponent::SetCanvasResolution", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } + + InvokeResize(static_cast(NewResolution.x), static_cast(NewResolution.y)); +} + +int FEVirtualUIComponent::GetWidth() const +{ + return Framebuffer->GetWidth(); +} + +int FEVirtualUIComponent::GetHeight() const +{ + return Framebuffer->GetHeight(); +} + +VIRTUAL_UI_RENDER_FUNCTION FEVirtualUIComponent::GetRenderFunction() +{ + return VirtualUI->GetOnRenderFunction(); +} + +void FEVirtualUIComponent::SetRenderFunction(VIRTUAL_UI_RENDER_FUNCTION UserRenderFunction) +{ + VirtualUI->SetOnRenderFunction(UserRenderFunction); +} + +void FEVirtualUIComponent::ClearRenderFunction() +{ + VirtualUI->ClearOnRenderFunction(); +} + +void FEVirtualUIComponent::UpdateCanvasTrianglePositions() +{ + if (ParentEntity == nullptr) + return; + + for (size_t i = 0; i < OriginalMeshTrianglePositions.size(); i++) + { + TransformedMeshTrianglePositions[i][0] = glm::vec3(ParentEntity->GetComponent().GetWorldMatrix() * glm::vec4(OriginalMeshTrianglePositions[i][0], 1.0f)); + TransformedMeshTrianglePositions[i][1] = glm::vec3(ParentEntity->GetComponent().GetWorldMatrix() * glm::vec4(OriginalMeshTrianglePositions[i][1], 1.0f)); + TransformedMeshTrianglePositions[i][2] = glm::vec3(ParentEntity->GetComponent().GetWorldMatrix() * glm::vec4(OriginalMeshTrianglePositions[i][2], 1.0f)); + } +} + +void FEVirtualUIComponent::UpdateInteractionRay(glm::dvec3 RayOrigin, glm::dvec3 RayDirection) +{ + InteractionRayOrigin = RayOrigin; + InteractionRayDirection = RayDirection; +} + +FEVirtualUI* FEVirtualUIComponent::GetVirtualUI() const +{ + return VirtualUI; +} + +bool FEVirtualUIComponent::IsInputActive() const +{ + return bActiveInput; +} + +void FEVirtualUIComponent::SetInputActive(bool NewValue) +{ + bActiveInput = NewValue; +} + +FETexture* FEVirtualUIComponent::GetCurrentFrameTexture() const +{ + return Framebuffer->GetColorAttachment(); +} + +void FEVirtualUIComponent::AddOnMouseButtonCallback(std::function UserOnMouseButtonCallback) +{ + VirtualUI->AddOnMouseButtonCallback(UserOnMouseButtonCallback); +} + +void FEVirtualUIComponent::InvokeMouseButton(int Button, int Action, int Mods) +{ + if (bActiveInput == false) + return; + + VirtualUI->InvokeMouseButton(Button, Action, Mods); +} + +void FEVirtualUIComponent::AddOnMouseMoveCallback(std::function UserOnMouseMoveCallback) +{ + VirtualUI->AddOnMouseMoveCallback(UserOnMouseMoveCallback); +} + +void FEVirtualUIComponent::InvokeMouseMove(double Xpos, double Ypos) +{ + if (bActiveInput == false) + return; + + VirtualUI->InvokeMouseMove(Xpos, Ypos); +} + +void FEVirtualUIComponent::AddOnResizeCallback(std::function UserOnResizeCallback) +{ + VirtualUI->AddOnResizeCallback(UserOnResizeCallback); +} + +void FEVirtualUIComponent::InvokeResize(int Width, int Height) +{ + if (Width <= 0 || Height <= 0) + { + LOG.Add("Invalid width or height in FEVirtualUIComponent::InvokeResize", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } + + delete Framebuffer; + Framebuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_COLOR_ATTACHMENT, Width, Height, false); + + VirtualUI->InvokeResize(Framebuffer->FBO, Width, Height); +} + +void FEVirtualUIComponent::AddOnCharCallback(std::function UserOnCharCallback) +{ + VirtualUI->AddOnCharCallback(UserOnCharCallback); +} + +void FEVirtualUIComponent::InvokeCharInput(unsigned int Codepoint) +{ + if (!bActiveInput) + return; + + VirtualUI->InvokeCharInput(Codepoint); +} + +void FEVirtualUIComponent::AddOnKeyCallback(std::function UserOnKeyCallback) +{ + VirtualUI->AddOnKeyCallback(UserOnKeyCallback); +} + +void FEVirtualUIComponent::InvokeKeyInput(const int Key, const int Scancode, const int Action, const int Mods) +{ + if (!bActiveInput) + return; + + VirtualUI->InvokeKeyInput(Key, Scancode, Action, Mods); +} + +void FEVirtualUIComponent::AddOnScrollCallback(std::function UserOnScrollCallback) +{ + VirtualUI->AddOnScrollCallback(UserOnScrollCallback); +} + +void FEVirtualUIComponent::InvokeScrollInput(const double Xoffset, const double Yoffset) +{ + if (!bActiveInput) + return; + + VirtualUI->InvokeScrollInput(Xoffset, Yoffset); +} + +void FEVirtualUIComponent::AddOnDropCallback(std::function UserOnDropCallback) +{ + VirtualUI->AddOnDropCallback(UserOnDropCallback); +} + +void FEVirtualUIComponent::InvokeDropInput(const int Count, const char** Paths) +{ + if (!bActiveInput) + return; + + VirtualUI->InvokeDropInput(Count, Paths); +} + +void FEVirtualUIComponent::MouseButtonListener(int Button, int Action, int Mods) +{ + if (!bActiveInput || !bRayColidingWithCanvas || !bMouseButtonPassThrough) + return; + + if (Button == GLFW_MOUSE_BUTTON_LEFT && Action == GLFW_PRESS) + { + InvokeMouseButton(ImGuiMouseButton_Left, GLFW_PRESS); + } + else if (Button == GLFW_MOUSE_BUTTON_LEFT && Action == GLFW_RELEASE) + { + InvokeMouseButton(ImGuiMouseButton_Left, GLFW_RELEASE); + } + else if (Button == GLFW_MOUSE_BUTTON_RIGHT && Action == GLFW_PRESS) + { + InvokeMouseButton(ImGuiMouseButton_Right, GLFW_PRESS); + } + else if (Button == GLFW_MOUSE_BUTTON_RIGHT && Action == GLFW_RELEASE) + { + InvokeMouseButton(ImGuiMouseButton_Right, GLFW_RELEASE); + } + else if (Button == GLFW_MOUSE_BUTTON_MIDDLE && Action == GLFW_PRESS) + { + InvokeMouseButton(ImGuiMouseButton_Middle, GLFW_PRESS); + } + else if (Button == GLFW_MOUSE_BUTTON_MIDDLE && Action == GLFW_RELEASE) + { + InvokeMouseButton(ImGuiMouseButton_Middle, GLFW_RELEASE); + } +} + +bool FEVirtualUIComponent::IsMouseButtonPassThroughActive() const +{ + return bMouseButtonPassThrough; +} + +bool FEVirtualUIComponent::SetMouseButtonPassThrough(bool NewValue) +{ + if (WindowToListen == nullptr) + { + LOG.Add("No window to listen in FEVirtualUIComponent::SetMouseButtonPassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); + return false; + } + + bMouseButtonPassThrough = NewValue; + return true; +} + +void FEVirtualUIComponent::AddOnMouseEnterCallback(std::function UserOnMouseEnterCallback) +{ + VirtualUI->AddOnMouseEnterCallback(UserOnMouseEnterCallback); +} + +void FEVirtualUIComponent::InvokeMouseEnterCallback(int Entered) +{ + if (!bActiveInput) + return; + + VirtualUI->InvokeMouseEnterCallback(Entered); +} + +bool FEVirtualUIComponent::InteractionRayToCanvasSpace(glm::dvec3 RayOrigin, glm::dvec3 RayDirection, glm::vec2* IntersectionPointInUVCanvasSpace, glm::vec3* IntersectionPointIn3DSpace) +{ + UpdateCanvasTrianglePositions(); + + for (size_t i = 0; i < TransformedMeshTrianglePositions.size(); i++) + { + std::vector CurrentTriangle; + CurrentTriangle.push_back(glm::dvec3(TransformedMeshTrianglePositions[i][0].x, TransformedMeshTrianglePositions[i][0].y, TransformedMeshTrianglePositions[i][0].z)); + CurrentTriangle.push_back(glm::dvec3(TransformedMeshTrianglePositions[i][1].x, TransformedMeshTrianglePositions[i][1].y, TransformedMeshTrianglePositions[i][1].z)); + CurrentTriangle.push_back(glm::dvec3(TransformedMeshTrianglePositions[i][2].x, TransformedMeshTrianglePositions[i][2].y, TransformedMeshTrianglePositions[i][2].z)); + double Distance = 0.0; + glm::dvec3 HitPoint = glm::dvec3(0.0); + double U = 0.0; + double V = 0.0; + + if (GEOMETRY.IsRayIntersectingTriangle(RayOrigin, RayDirection, CurrentTriangle, Distance, &HitPoint, &U, &V)) + { + if (IntersectionPointIn3DSpace != nullptr) + *IntersectionPointIn3DSpace = HitPoint; + + // Load texture coordinates of the triangle vertices. + glm::dvec2 uv0 = MeshTriangleUVs[i][0]; + glm::dvec2 uv1 = MeshTriangleUVs[i][1]; + glm::dvec2 uv2 = MeshTriangleUVs[i][2]; + + // Calculate texture coordinates of the hit point using interpolation. + glm::dvec2 HitUV = (1.0 - U - V) * uv0 + U * uv1 + V * uv2; + *IntersectionPointInUVCanvasSpace = HitUV; + + InvokeMouseEnterCallback(1); + bRayColidingWithCanvas = true; + return true; + } + } + + InvokeMouseEnterCallback(0); + bRayColidingWithCanvas = false; + return false; +} + +FEAABB FEVirtualUIComponent::GetAABB() const +{ + FEAABB Result; + + if (CanvasMesh == nullptr) + return Result; + + Result = CanvasMesh->GetAABB(); + + if (ParentEntity == nullptr) + return Result; + + Result = Result.Transform(ParentEntity->GetComponent().GetWorldMatrix()); + + return Result; +} + +void FEVirtualUIComponent::MouseMoveListener(double Xpos, double Ypos) +{ + // Not useing bRayColidingWithCanvas here because we want to call UpdateRayIntersection() to determine bRayColidingWithCanvas value. + if (!bActiveInput || !bMouseMovePassThrough) + return; + + UpdateRayIntersection(); +} + +void FEVirtualUIComponent::UpdateRayIntersection() +{ + glm::vec2 HitUV = glm::vec2(0.0f); + if (InteractionRayToCanvasSpace(InteractionRayOrigin, InteractionRayDirection, &HitUV)) + InvokeMouseMove(HitUV[0] * GetWidth(), (1.0 - HitUV[1]) * GetHeight()); +} + +bool FEVirtualUIComponent::IsMouseMovePassThroughActive() const +{ + return bMouseMovePassThrough; +} + +bool FEVirtualUIComponent::SetMouseMovePassThrough(bool NewValue) +{ + if (WindowToListen == nullptr) + { + LOG.Add("No window to listen in FEVirtualUIComponent::SetMouseMovePassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); + return false; + } + + bMouseMovePassThrough = NewValue; + return true; +} + +void FEVirtualUIComponent::CharListener(unsigned int Codepoint) +{ + if (!bActiveInput || !bRayColidingWithCanvas || !bCharPassThrough) + return; + + InvokeCharInput(Codepoint); +} + +bool FEVirtualUIComponent::IsCharPassThroughActive() const +{ + return bCharPassThrough; +} + +bool FEVirtualUIComponent::SetCharPassThrough(bool NewValue) +{ + if (WindowToListen == nullptr) + { + LOG.Add("No window to listen in FEVirtualUIComponent::SetCharPassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); + return false; + } + + bCharPassThrough = NewValue; + return true; +} + +void FEVirtualUIComponent::KeyListener(int Key, int Scancode, int Action, int Mods) +{ + if (!bActiveInput || !bRayColidingWithCanvas || !bKeyPassThrough) + return; + + InvokeKeyInput(Key, Scancode, Action, Mods); +} + +bool FEVirtualUIComponent::IsKeyPassThroughActive() const +{ + return bKeyPassThrough; +} + +bool FEVirtualUIComponent::SetKeyPassThrough(bool NewValue) +{ + if (WindowToListen == nullptr) + { + LOG.Add("No window to listen in FEVirtualUIComponent::SetKeyPassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); + return false; + } + + bKeyPassThrough = NewValue; + return true; +} + +void FEVirtualUIComponent::DropListener(int Count, const char** Paths) +{ + if (!bActiveInput || !bRayColidingWithCanvas || !bDropPassThrough) + return; + + InvokeDropInput(Count, Paths); +} + +bool FEVirtualUIComponent::IsDropPassThroughActive() const +{ + return bDropPassThrough; +} + +bool FEVirtualUIComponent::SetDropPassThrough(bool NewValue) +{ + if (WindowToListen == nullptr) + { + LOG.Add("No window to listen in FEVirtualUIComponent::SetDropPassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); + return false; + } + + bDropPassThrough = NewValue; + return true; +} + +void FEVirtualUIComponent::ScrollListener(double Xoffset, double Yoffset) +{ + if (!bActiveInput || !bRayColidingWithCanvas || !bScrollPassThrough) + return; + + InvokeScrollInput(Xoffset, Yoffset); +} + +bool FEVirtualUIComponent::IsScrollPassThroughActive() const +{ + return bScrollPassThrough; +} + +bool FEVirtualUIComponent::SetScrollPassThrough(bool NewValue) +{ + if (WindowToListen == nullptr) + { + LOG.Add("No window to listen in FEVirtualUIComponent::SetScrollPassThrough", "FE_LOG_INPUT", FE_LOG_ERROR); + return false; + } + + bScrollPassThrough = NewValue; + return true; +} + +void FEVirtualUIComponent::RegisterCallbacksForWindow() +{ + if (WindowToListen == nullptr) + return; + + auto MouseButtonCallbackToRegister = std::bind(&FEVirtualUIComponent::MouseButtonListener, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); + MouseButtonListenerID = WindowToListen->AddOnMouseButtonCallback(MouseButtonCallbackToRegister); + + auto MouseMoveCallbackToRegister = std::bind(&FEVirtualUIComponent::MouseMoveListener, this, std::placeholders::_1, std::placeholders::_2); + MouseMoveListenerID = WindowToListen->AddOnMouseMoveCallback(MouseMoveCallbackToRegister); + + auto CharCallbackToRegister = std::bind(&FEVirtualUIComponent::CharListener, this, std::placeholders::_1); + CharListenerID = WindowToListen->AddOnCharCallback(CharCallbackToRegister); + + auto KeyCallbackToRegister = std::bind(&FEVirtualUIComponent::KeyListener, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); + KeyListenerID = WindowToListen->AddOnKeyCallback(KeyCallbackToRegister); + + auto DropCallbackToRegister = std::bind(&FEVirtualUIComponent::DropListener, this, std::placeholders::_1, std::placeholders::_2); + DropListenerID = WindowToListen->AddOnDropCallback(DropCallbackToRegister); + + auto ScrollCallbackToRegister = std::bind(&FEVirtualUIComponent::ScrollListener, this, std::placeholders::_1, std::placeholders::_2); + ScrollListenerID = WindowToListen->AddOnScrollCallback(ScrollCallbackToRegister); +} + +void FEVirtualUIComponent::UnregisterCallbacksForWindow() +{ + if (WindowToListen == nullptr) + return; + + WindowToListen->RemoveCallback(MouseButtonListenerID); + WindowToListen->RemoveCallback(MouseMoveListenerID); + WindowToListen->RemoveCallback(CharListenerID); + WindowToListen->RemoveCallback(KeyListenerID); + WindowToListen->RemoveCallback(DropListenerID); + WindowToListen->RemoveCallback(ScrollListenerID); +} + +void FEVirtualUIComponent::SetWindowToListen(FEWindow* Window) +{ + if (WindowToListen != nullptr && WindowToListen != Window) + UnregisterCallbacksForWindow(); + + WindowToListen = Window; + RegisterCallbacksForWindow(); +} +FEWindow* FEVirtualUIComponent::GetWindowToListen() const +{ + return WindowToListen; +} + +bool FEVirtualUIComponent::IsVisible() const +{ + //return CanvasEntity->GetComponent().IsVisible(); + return true; +} + +void FEVirtualUIComponent::SetVisibility(bool NewValue) +{ + //CanvasEntity->GetComponent().SetVisibility(NewValue); +} + +void FEVirtualUIComponent::ExecuteFunctionToAddFont(std::function Func, std::function CallbackOnFontReady) +{ + VirtualUI->ExecuteFunctionToAddFont(Func, CallbackOnFontReady); +} \ No newline at end of file diff --git a/SubSystems/FEVirtualUIContext.h b/SubSystems/Scene/Components/FEVirtualUIComponent.h similarity index 78% rename from SubSystems/FEVirtualUIContext.h rename to SubSystems/Scene/Components/FEVirtualUIComponent.h index 5c20c97..f854230 100644 --- a/SubSystems/FEVirtualUIContext.h +++ b/SubSystems/Scene/Components/FEVirtualUIComponent.h @@ -1,91 +1,42 @@ -#include "FEVirtualUI.h" -#include "../ResourceManager/FEResourceManager.h" +#pragma once +#include "../Renderer/FEGameModel.h" namespace FocalEngine { - // Not designed to work with canvases a lot more complex than a plane. - class FEVirtualUIContext : public FEObject +#define VIRTUAL_UI_RENDER_FUNCTION std::function + struct FEVirtualUIComponent { - friend class FEScene; - friend class FERenderer; - - FEVirtualUIContext(int Width = 1280, int Height = 720, FEMesh* SampleMesh = nullptr, std::string Name = "UnNamed"); - ~FEVirtualUIContext(); - - FEVirtualUI* VirtualUI = nullptr; - FEFramebuffer* Framebuffer = nullptr; - - // Canvas - FEMesh* CanvasMesh = nullptr; - FEMaterial* CanvasMaterial = nullptr; - FEGameModel* CanvasGameModel = nullptr; - FEPrefab* CanvasPrefab = nullptr; - public: - FEEntity* CanvasEntity = nullptr; private: - - // Mesh interaction - std::vector> OriginalMeshTrianglePositions; - std::vector> TransformedMeshTrianglePositions; - std::vector> MeshTriangleUVs; - void UpdateCanvasTrianglePositions(); - - glm::dvec3 InteractionRayOrigin = glm::dvec3(0.0); - glm::dvec3 InteractionRayDirection = glm::dvec3(0.0); - - // Input - bool bActiveInput = true; - bool bRayColidingWithCanvas = false; - FEWindow* WindowToListen = nullptr; - void RegisterCallbacksForWindow(); - void UnregisterCallbacksForWindow(); - - void MouseButtonListener(int Button, int Action, int Mods); - bool bMouseButtonPassThrough = false; - void MouseMoveListener(double Xpos, double Ypos); - bool bMouseMovePassThrough = false; - void CharListener(unsigned int Codepoint); - bool bCharPassThrough = false; - void KeyListener(int Key, int Scancode, int Action, int Mods); - bool bKeyPassThrough = false; - void DropListener(int Count, const char** Paths); - bool bDropPassThrough = false; - void ScrollListener(double Xoffset, double Yoffset); - bool bScrollPassThrough = false; - - bool InteractionRayToCanvasSpace(glm::dvec3 RayOrigin, glm::dvec3 RayDirection, glm::vec2* IntersectionPointInUVCanvasSpace, glm::vec3* IntersectionPointIn3DSpace = nullptr); - void InvokeMouseEnterCallback(int Entered); + friend class FEVirtualUISystem; public: + FEVirtualUIComponent(int Width = 1024, int Height = 1024, FEMesh* CanvasMesh = nullptr); + ~FEVirtualUIComponent(); + // Prevent copying and assignment - FEVirtualUIContext(const FEVirtualUIContext&) = delete; - FEVirtualUIContext& operator=(const FEVirtualUIContext&) = delete; + FEVirtualUIComponent(const FEVirtualUIComponent&) = delete; + FEVirtualUIComponent& operator=(const FEVirtualUIComponent&) = delete; - void GetSize(int* Width, int* Height) const; - void SetSize(int NewWidth, int NewHeight); + glm::vec2 GetCanvasResolution() const; + void SetCanvasResolution(glm::vec2 NewResolution); int GetWidth() const; int GetHeight() const; - glm::vec3 GetPosition() const; - void SetPosition(glm::vec3 NewPosition); - - glm::vec3 GetRotation() const; - void SetRotation(glm::vec3 NewRotation); - - glm::vec3 GetScale() const; - void SetScale(glm::vec3 NewScale); - bool IsVisible() const; void SetVisibility(bool NewValue); bool IsInputActive() const; void SetInputActive(bool NewValue); - std::function GetRenderFunction(); - void SetRenderFunction(std::function UserRenderFunction); + VIRTUAL_UI_RENDER_FUNCTION GetRenderFunction(); + void SetRenderFunction(VIRTUAL_UI_RENDER_FUNCTION UserRenderFunction); void ClearRenderFunction(); FETexture* GetCurrentFrameTexture() const; + FEMesh* GetCanvasMesh() const; + void SetCanvasMesh(FEMesh* NewCanvasMesh); + FEAABB GetAABB() const; + // Event Handling void AddOnResizeCallback(std::function UserOnResizeCallback); void AddOnMouseEnterCallback(std::function UserOnMouseEnterCallback); @@ -100,7 +51,7 @@ namespace FocalEngine void InvokeResize(int Width, int Height); void InvokeMouseButton(int Button, int Action, int Mods = 0); void InvokeMouseMove(double Xpos, double Ypos); - void InvokeCharInput(unsigned int codepoint); + void InvokeCharInput(unsigned int Codepoint); void InvokeKeyInput(int Key, int Scancode, int Action, int Mods); void InvokeDropInput(int Count, const char** Paths); void InvokeScrollInput(double Xoffset, double Yoffset); @@ -141,5 +92,54 @@ namespace FocalEngine // Use that instead of directly adding fonts to ImGui // to ensure that font addition is done in the correct way. void ExecuteFunctionToAddFont(std::function Func, std::function CallbackOnFontReady); + private: + FEEntity* ParentEntity = nullptr; + + FEVirtualUI* VirtualUI = nullptr; + FEFramebuffer* Framebuffer = nullptr; + FEMesh* CanvasMesh = nullptr; + + // Mesh interaction + std::vector> OriginalMeshTrianglePositions; + std::vector> TransformedMeshTrianglePositions; + std::vector> MeshTriangleUVs; + void UpdateCanvasTrianglePositions(); + + glm::dvec3 InteractionRayOrigin = glm::dvec3(0.0); + glm::dvec3 InteractionRayDirection = glm::dvec3(0.0); + + // Input + bool bActiveInput = false; + bool bRayColidingWithCanvas = false; + FEWindow* WindowToListen = nullptr; + void RegisterCallbacksForWindow(); + void UnregisterCallbacksForWindow(); + + std::string MouseButtonListenerID = ""; + void MouseButtonListener(int Button, int Action, int Mods); + bool bMouseButtonPassThrough = false; + + std::string MouseMoveListenerID = ""; + void MouseMoveListener(double Xpos, double Ypos); + bool bMouseMovePassThrough = false; + + std::string CharListenerID = ""; + void CharListener(unsigned int Codepoint); + bool bCharPassThrough = false; + + std::string KeyListenerID = ""; + void KeyListener(int Key, int Scancode, int Action, int Mods); + bool bKeyPassThrough = false; + + std::string DropListenerID = ""; + void DropListener(int Count, const char** Paths); + bool bDropPassThrough = false; + + std::string ScrollListenerID = ""; + void ScrollListener(double Xoffset, double Yoffset); + bool bScrollPassThrough = false; + + bool InteractionRayToCanvasSpace(glm::dvec3 RayOrigin, glm::dvec3 RayDirection, glm::vec2* IntersectionPointInUVCanvasSpace, glm::vec3* IntersectionPointIn3DSpace = nullptr); + void InvokeMouseEnterCallback(int Entered); }; } \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.cpp b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.cpp new file mode 100644 index 0000000..3e387c3 --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.cpp @@ -0,0 +1,90 @@ +#include "FENativeScriptComponent.h" +#include "../../FEScene.h" +using namespace FocalEngine; + +void FENativeScriptComponent::Awake() +{ + CoreInstance->Awake(); +} + +void FENativeScriptComponent::OnDestroy() +{ + if (CoreInstance == nullptr) + return; + + CoreInstance->OnDestroy(); + delete CoreInstance; + CoreInstance = nullptr; +} + +void FENativeScriptComponent::OnUpdate(double DeltaTime) +{ + CoreInstance->OnUpdate(DeltaTime); +} + +bool FENativeScriptComponent::IsInitialized() +{ + return CoreInstance != nullptr; +} + +std::string FENativeScriptComponent::GetModuleID() +{ + return ModuleID; +} + +class FENativeScriptCore* FENativeScriptComponent::GetCoreInstance() const +{ + return CoreInstance; +} + +const struct FEScriptData* FENativeScriptComponent::GetScriptData() const +{ + return ScriptData; +} + +bool FENativeScriptComponent::SetVariableValue(std::string VariableName, std::any Value) +{ + if (CoreInstance == nullptr) + return false; + + if (ScriptData->VariablesRegistry.find(VariableName) == ScriptData->VariablesRegistry.end()) + return false; + + try + { + ScriptData->VariablesRegistry[VariableName].Setter(CoreInstance, Value); + return true; + } + catch (const std::bad_any_cast&) + { + LOG.Add("FENativeScriptComponent::SetVariableValue: Error setting value for '" + VariableName + "'", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + return true; +} + +std::any FENativeScriptComponent::GetVariableValueRaw(const std::string& VariableName) const +{ + if (CoreInstance == nullptr || ScriptData == nullptr) + return std::any(); + + auto VariablesIterator = ScriptData->VariablesRegistry.find(VariableName); + if (VariablesIterator == ScriptData->VariablesRegistry.end()) + { + LOG.Add("FENativeScriptComponent::GetVariableValueRaw: Variable with name: " + VariableName + " not found in registry.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::any(); + } + + return VariablesIterator->second.Getter(CoreInstance); +} + +bool FENativeScriptComponent::IsFailedToLoad() +{ + return FailedToLoadData != nullptr; +} + +FENativeScriptFailedToLoadData* FENativeScriptComponent::GetFailedToLoadData() +{ + return FailedToLoadData; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.h b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.h new file mode 100644 index 0000000..7798b9f --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.h @@ -0,0 +1,54 @@ +#pragma once + +#include "FENativeScriptCore.h" + +namespace FocalEngine +{ + class FENativeScriptFailedToLoadData + { + friend class FENativeScriptSystem; + + Json::Value RawData; + std::string ModuleID; + public: + Json::Value GetRawData() + { + return RawData; + } + + std::string GetModuleID() + { + return ModuleID; + } + }; + + class FENativeScriptComponent + { + friend class FENativeScriptSystem; + + class FENativeScriptCore* CoreInstance = nullptr; + std::string ModuleID; + struct FEScriptData* ScriptData = nullptr; + FENativeScriptFailedToLoadData* FailedToLoadData = nullptr; + public: + bool IsInitialized(); + std::string GetModuleID(); + + class FENativeScriptCore* GetCoreInstance() const; + const struct FEScriptData* GetScriptData() const; + + std::any GetVariableValueRaw(const std::string& VariableName) const; + + template + bool GetVariableValue(const std::string& VariableName, T& OutValue) const; + bool SetVariableValue(std::string VariableName, std::any Value); + + void Awake(); + void OnDestroy(); + void OnUpdate(double DeltaTime); + + bool IsFailedToLoad(); + FENativeScriptFailedToLoadData* GetFailedToLoadData(); + }; +#include "FENativeScriptComponent.inl" +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.inl b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.inl new file mode 100644 index 0000000..81bc08f --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptComponent.inl @@ -0,0 +1,28 @@ +#pragma once + +template +bool FENativeScriptComponent::GetVariableValue(const std::string& VariableName, T& OutValue) const +{ + if (CoreInstance == nullptr || ScriptData == nullptr) + return false; + + auto VariablesIterator = ScriptData->VariablesRegistry.find(VariableName); + if (VariablesIterator == ScriptData->VariablesRegistry.end()) + { + LOG.Add("FENativeScriptComponent::GetVariableValueRaw: Variable with name: " + VariableName + " not found in registry.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + const auto& VariableInfo = VariablesIterator->second; + try + { + std::any Value = VariableInfo.Getter(CoreInstance); + OutValue = std::any_cast(Value); + return true; + } + catch (const std::bad_any_cast&) + { + LOG.Add("FENativeScriptComponent::GetVariableValue: Error std::bad_any_cast for '" + VariableName + "'", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptCore.cpp b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptCore.cpp new file mode 100644 index 0000000..9f55418 --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptCore.cpp @@ -0,0 +1,2 @@ +#include "FENativeScriptCore.h" +using namespace FocalEngine; \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptCore.h b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptCore.h new file mode 100644 index 0000000..2c31cb8 --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptCore.h @@ -0,0 +1,48 @@ +#pragma once + +#include "../Core/FEObject.h" +#include "../Core/FEGeometricTools.h" +#include + +namespace FocalEngine +{ + class FENativeScriptCore + { + friend class FENativeScriptComponent; + friend class FENativeScriptSystem; + public: + virtual ~FENativeScriptCore() = default; + virtual void Awake() {} + virtual void OnDestroy() {} + virtual void OnUpdate(double DeltaTime) {} + + class FEEntity* ParentEntity; + }; + + struct FEScriptVariableInfo + { + std::string Name; + std::string Type; + std::function Getter; + std::function Setter; + }; + + struct FEScriptData + { + std::function ConstructorFunction; + std::string Name; + bool bRunInEditor = false; + std::unordered_map VariablesRegistry; + }; + + struct FENativeScriptModuleData + { + std::string Name; + std::string DLLPath; + std::string PDBPath; + std::string ID; + + HMODULE DLLHandle; + std::unordered_map Registry; + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptModule.cpp b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptModule.cpp new file mode 100644 index 0000000..a07b64b --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptModule.cpp @@ -0,0 +1,139 @@ +#include "FENativeScriptModule.h" +#include "FENativeScriptProject.h" +using namespace FocalEngine; + +FENativeScriptModule::FENativeScriptModule() : FEObject(FE_OBJECT_TYPE::FE_NATIVE_SCRIPT_MODULE, "Unnamed native script module") +{ + ScriptAssetPackage = new FEAssetPackage(); + Project = new FENativeScriptProject(); + Project->Parent = this; +}; + +FENativeScriptModule::FENativeScriptModule(std::string DebugDLLFilePath, std::string DebugPDBFilePath, std::string ReleaseDLLFilePath, std::vector ScriptFiles) : FEObject(FE_OBJECT_TYPE::FE_NATIVE_SCRIPT_MODULE, "Unnamed native script module") +{ + ScriptAssetPackage = new FEAssetPackage(); + DebugDLLAssetID = ScriptAssetPackage->ImportAssetFromFile(DebugDLLFilePath); + DebugPDBAssetID = ScriptAssetPackage->ImportAssetFromFile(DebugPDBFilePath); + ReleaseDLLAssetID = ScriptAssetPackage->ImportAssetFromFile(ReleaseDLLFilePath); + + ScriptAssetIDs.resize(ScriptFiles.size()); + for (size_t i = 0; i < ScriptFiles.size(); i++) + { + ScriptAssetIDs[i] = ScriptAssetPackage->ImportAssetFromFile(ScriptFiles[i]); + } + + Project = new FENativeScriptProject(); + Project->Parent = this; +} + +bool FENativeScriptModule::UpdateFiles(std::string DebugDLLFilePath, std::string DebugPDBFilePath, std::string ReleaseDLLFilePath, std::vector ScriptFiles) +{ + // First we will check if all files are valid + if (!FILE_SYSTEM.DoesFileExist(DebugDLLFilePath)) + { + LOG.Add("FENativeScriptModule::Update: DebugDLLFilePath does not exist", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (!FILE_SYSTEM.DoesFileExist(DebugPDBFilePath)) + { + LOG.Add("FENativeScriptModule::Update: DebugPDBFilePath does not exist", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (!FILE_SYSTEM.DoesFileExist(ReleaseDLLFilePath)) + { + LOG.Add("FENativeScriptModule::Update: ReleaseDLLFilePath does not exist", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + for (size_t i = 0; i < ScriptFiles.size(); i++) + { + if (!FILE_SYSTEM.DoesFileExist(ScriptFiles[i])) + { + LOG.Add("FENativeScriptModule::Update: ScriptFiles[" + std::to_string(i) + "] does not exist", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + + // Than we can start updating the files + if (ScriptAssetPackage->IsAssetIDPresent(DebugDLLAssetID)) + { + if (!ScriptAssetPackage->UpdateAssetFromFile(DebugDLLAssetID, DebugDLLFilePath)) + { + LOG.Add("FENativeScriptModule::Update: Failed to update DebugDLLAssetID", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + else + { + DebugDLLAssetID = ScriptAssetPackage->ImportAssetFromFile(DebugDLLFilePath); + if (DebugDLLAssetID.empty()) + { + LOG.Add("FENativeScriptModule::Update: Failed to import DebugDLLAssetID", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + + if (ScriptAssetPackage->IsAssetIDPresent(DebugPDBAssetID)) + { + if (!ScriptAssetPackage->UpdateAssetFromFile(DebugPDBAssetID, DebugPDBFilePath)) + { + LOG.Add("FENativeScriptModule::Update: Failed to update DebugPDBAssetID", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + else + { + DebugPDBAssetID = ScriptAssetPackage->ImportAssetFromFile(DebugPDBFilePath); + if (DebugPDBAssetID.empty()) + { + LOG.Add("FENativeScriptModule::Update: Failed to import DebugPDBAssetID", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + + if (ScriptAssetPackage->IsAssetIDPresent(ReleaseDLLAssetID)) + { + if (!ScriptAssetPackage->UpdateAssetFromFile(ReleaseDLLAssetID, ReleaseDLLFilePath)) + { + LOG.Add("FENativeScriptModule::Update: Failed to update ReleaseDLLAssetID", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + else + { + ReleaseDLLAssetID = ScriptAssetPackage->ImportAssetFromFile(ReleaseDLLFilePath); + if (ReleaseDLLAssetID.empty()) + { + LOG.Add("FENativeScriptModule::Update: Failed to import ReleaseDLLAssetID", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + + // Script files could be added or removed + // So we will remove all of them and add them again + for (size_t i = 0; i < ScriptAssetIDs.size(); i++) + { + ScriptAssetPackage->RemoveAsset(ScriptAssetIDs[i]); + } + ScriptAssetIDs.resize(ScriptFiles.size()); + for (size_t i = 0; i < ScriptFiles.size(); i++) + { + ScriptAssetIDs[i] = ScriptAssetPackage->ImportAssetFromFile(ScriptFiles[i]); + } + + return true; +} + +FENativeScriptModule::~FENativeScriptModule() {} + +bool FENativeScriptModule::IsLoadedToMemory() +{ + return bIsLoadedToMemory; +} + +FENativeScriptProject* FENativeScriptModule::GetProject() const +{ + return Project; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptModule.h b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptModule.h new file mode 100644 index 0000000..b082a20 --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptModule.h @@ -0,0 +1,45 @@ +#pragma once + +#include "FENativeScriptCore.h" +#include "../../../FileSystem/FEAssetPackage.h" + +namespace FocalEngine +{ + class FENativeScriptProject; + class FENativeScriptModule : public FEObject + { + friend class FEResourceManager; + friend class FENativeScriptSystem; + friend class FENativeScriptProject; + + FENativeScriptModule(); + FENativeScriptModule(std::string DebugDLLFilePath, std::string DebugPDBFilePath, std::string ReleaseDLLFilePath, std::vector ScriptFiles = {}); + + bool UpdateFiles(std::string DebugDLLFilePath, std::string DebugPDBFilePath, std::string ReleaseDLLFilePath, std::vector ScriptFiles = {}); + public: + ~FENativeScriptModule(); + + bool IsLoadedToMemory(); + FENativeScriptProject* GetProject() const; + private: + FEAssetPackage* ScriptAssetPackage; + + std::string DebugDLLAssetID; + std::string DebugPDBAssetID; + + std::string ReleaseDLLAssetID; + std::string ReleasePDBAssetID; + + std::string CMakeFileAssetID; + std::vector ScriptAssetIDs; + + bool bIsLoadedToMemory = false; + HMODULE DLLHandle = nullptr; + std::unordered_map Registry; + + std::string ExtractedDLLPath; + std::string ExtractedPDBPath; + + FENativeScriptProject* Project = nullptr; + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptProject.cpp b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptProject.cpp new file mode 100644 index 0000000..316c954 --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptProject.cpp @@ -0,0 +1,852 @@ +#include "FENativeScriptProject.h" +#include +using namespace FocalEngine; + +FENativeScriptProject::FENativeScriptProject() +{ + +} + +void FENativeScriptProject::SetWorkingDirectory(std::string NewValue) +{ + if (NewValue.empty()) + { + LOG.Add("FENativeScriptProject::SetWorkingDirectory: NewValue is empty!", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + + if (Parent == nullptr) + { + LOG.Add("FENativeScriptProject::SetWorkingDirectory: Parent is nullptr!", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + + if (!FILE_SYSTEM.DoesDirectoryExist(NewValue)) + { + LOG.Add("FENativeScriptProject::SetWorkingDirectory: NewValue does not exist!", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + + WorkingDirectory = NewValue; + VSProjectDirectory = WorkingDirectory + "NativeScriptProjects/" + Parent->GetObjectID() + "/"; +} + +std::string FENativeScriptProject::GetWorkingDirectory() +{ + return WorkingDirectory; +} + +std::string FENativeScriptProject::GetVSProjectDirectory() +{ + return VSProjectDirectory; +} + +std::string FENativeScriptProject::GetVSProjectName() +{ + if (Parent == nullptr) + { + LOG.Add("FENativeScriptProject::GetVSProjectName: Parent is nullptr!", "FE_LOG_LOADING", FE_LOG_ERROR); + return ""; + } + + std::string AppropriateProjectName = Parent->GetName(); + for (size_t i = 0; i < AppropriateProjectName.size(); i++) + { + if (AppropriateProjectName[i] == ' ') + AppropriateProjectName[i] = '_'; + } + + if (AppropriateProjectName.empty()) + AppropriateProjectName = "UntitledProject"; + + return AppropriateProjectName; +} + +FENativeScriptProject::~FENativeScriptProject() +{ + if (DataToRecoverVSProject != nullptr) + delete DataToRecoverVSProject; +} + +bool FENativeScriptProject::Initialize(FEAssetPackage* Data) +{ + if (Data == nullptr) + { + LOG.Add("FENativeScriptProject::Initialize: Data is nullptr!", "FE_LOG_LOADING", FE_LOG_ERROR); + return false; + } + + DataToRecoverVSProject = Data; + + std::vector SourceFilesList = DataToRecoverVSProject->GetEntryList(); + for (size_t i = 0; i < SourceFilesList.size(); i++) + { + SourceFileList.push_back(SourceFilesList[i].Name); + } + + SetFileTracking(); + return true; +} + +void FENativeScriptProject::UpdateDataToRecoverVSProject() +{ + if (DataToRecoverVSProject == nullptr) + { + if (!FILE_SYSTEM.DoesDirectoryExist(VSProjectDirectory)) + { + LOG.Add("FENativeScriptProject::Save: VSProjectDirectory does not exist and DataToRecoverVSProject is nullptr!", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + + DataToRecoverVSProject = new FEAssetPackage(); + for (size_t i = 0; i < SourceFileList.size(); i++) + { + std::string FullFilePath = VSProjectDirectory + SourceFileList[i]; + if (!FILE_SYSTEM.DoesFileExist(FullFilePath)) + { + LOG.Add("FENativeScriptProject::Save: File does not exist!", "FE_LOG_LOADING", FE_LOG_ERROR); + continue; + } + + FEAssetPackageEntryIntializeData EntryData; + EntryData.Name = SourceFileList[i]; + EntryData.Type = "Text"; + DataToRecoverVSProject->ImportAssetFromFile(FullFilePath, EntryData); + } + } + else + { + if (!FILE_SYSTEM.DoesDirectoryExist(VSProjectDirectory)) + { + LOG.Add("FENativeScriptProject::Save: VSProjectDirectory does not exist and saving old DataToRecoverVSProject!", "FE_LOG_LOADING", FE_LOG_WARNING); + } + else + { + std::vector OldSourceFilesList = DataToRecoverVSProject->GetEntryList(); + for (size_t i = 0; i < SourceFileList.size(); i++) + { + std::string FullFilePath = VSProjectDirectory + SourceFileList[i]; + if (!FILE_SYSTEM.DoesFileExist(FullFilePath)) + { + LOG.Add("FENativeScriptProject::Save: File does not exist!", "FE_LOG_LOADING", FE_LOG_ERROR); + continue; + } + + for (size_t j = 0; j < OldSourceFilesList.size(); j++) + { + if (OldSourceFilesList[j].Name == SourceFileList[i]) + { + if (!DataToRecoverVSProject->UpdateAssetFromFile(OldSourceFilesList[j].ID, FullFilePath)) + { + LOG.Add("FENativeScriptProject::Save: Failed to update asset!", "FE_LOG_LOADING", FE_LOG_ERROR); + } + break; + } + } + } + } + } +} + +bool FENativeScriptProject::UpdateParentScriptModule() +{ + if (Parent == nullptr) + { + LOG.Add("FENativeScriptProject::UpdateParentScriptModule: Parent is nullptr", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + std::vector FullPathSourceFileList; + for (size_t i = 0; i < SourceFileList.size(); i++) + { + FullPathSourceFileList.push_back(VSProjectDirectory + SourceFileList[i]); + } + + if (!Parent->UpdateFiles(DebugDllFileData.Path, DebugPdbFileData.Path, ReleaseDllFileData.Path, FullPathSourceFileList)) + { + LOG.Add("FENativeScriptProject::UpdateParentScriptModule: Error updating parent native script module", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (!NATIVE_SCRIPT_SYSTEM.ReloadDLL(Parent)) + { + LOG.Add("FENativeScriptProject::UpdateParentScriptModule: Error reloading parent native script module", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + return true; +} + +bool FENativeScriptProject::HasRecoverableVSProjectData() +{ + if (DataToRecoverVSProject == nullptr) + return false; + + std::vector AssetPackageContent = DataToRecoverVSProject->GetEntryList(); + if (AssetPackageContent.empty()) + return false; + + return true; +} + +bool FENativeScriptProject::IsVSProjectFolderValidAndIntact() +{ + if (Parent == nullptr) + { + LOG.Add("FENativeScriptProject::IsVSProjectValid: Parent is nullptr", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (VSProjectDirectory.empty()) + { + LOG.Add("FENativeScriptProject::IsVSProjectValid: Path is empty", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (!FILE_SYSTEM.DoesDirectoryExist(VSProjectDirectory)) + { + LOG.Add("FENativeScriptProject::IsVSProjectValid: Path does not exist", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + std::vector InitialFilesToCheck = { + "CMakeLists.txt", + "FocalEngine.lib", "FEBasicApplication.lib", + "BuildManagement/EnsureBuildCompletion.cmake", + "BuildManagement/DebugBuildActions.cmake", "BuildManagement/ReleaseBuildActions.cmake" + }; + + for (size_t i = 0; i < InitialFilesToCheck.size(); i++) + { + if (!FILE_SYSTEM.DoesFileExist(VSProjectDirectory + InitialFilesToCheck[i])) + { + LOG.Add("FENativeScriptProject::IsVSProjectValid: File " + InitialFilesToCheck[i] + " does not exist", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + } + + std::string AppropriateProjectName = GetVSProjectName(); + std::vector VSProjectFilesToCheck = { + AppropriateProjectName + ".sln", AppropriateProjectName + ".vcxproj", AppropriateProjectName + ".vcxproj.filters" + }; + + for (size_t i = 0; i < VSProjectFilesToCheck.size(); i++) + { + if (!FILE_SYSTEM.DoesFileExist(VSProjectDirectory + VSProjectFilesToCheck[i])) + { + LOG.Add("FENativeScriptProject::IsVSProjectValid: File " + VSProjectFilesToCheck[i] + " does not exist", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + } + + if (SourceFileList.empty()) + return false; + + for (size_t i = 0; i < SourceFileList.size(); i++) + { + if (!FILE_SYSTEM.DoesFileExist(VSProjectDirectory + SourceFileList[i])) + { + LOG.Add("FENativeScriptProject::IsVSProjectValid: File " + SourceFileList[i] + " does not exist", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + } + + return true; +} + +bool FENativeScriptProject::EnsureVSProjectDirectoryIsIntact() +{ + if (Parent == nullptr) + { + LOG.Add("FENativeScriptProject::EnsureVSProjectDirectoryIsIntact: Parent is nullptr", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (VSProjectDirectory.empty()) + { + LOG.Add("FENativeScriptProject::EnsureVSProjectDirectoryIsIntact: Path is empty", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (!FILE_SYSTEM.DoesDirectoryExist(WorkingDirectory + "NativeScriptProjects/")) + { + if (!FILE_SYSTEM.CreateDirectory(WorkingDirectory + "NativeScriptProjects/")) + { + LOG.Add("FENativeScriptProject::EnsureVSProjectDirectoryIsIntact: Error creating \"NativeScriptProjects/\" directory", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + + if (VSProjectDirectory != WorkingDirectory + "NativeScriptProjects/" + Parent->GetObjectID() + "/") + VSProjectDirectory = WorkingDirectory + "NativeScriptProjects/" + Parent->GetObjectID() + "/"; + + if (FILE_SYSTEM.DoesDirectoryExist(VSProjectDirectory)) + { + if (!FILE_SYSTEM.DeleteDirectory(VSProjectDirectory)) + { + LOG.Add("FENativeScriptProject::EnsureVSProjectDirectoryIsIntact: Error deleting old directory", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + + if (!FILE_SYSTEM.CreateDirectory(VSProjectDirectory)) + { + LOG.Add("FENativeScriptProject::EnsureVSProjectDirectoryIsIntact: Error creating directory", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + return true; +} + +bool FENativeScriptProject::RecreateVSProject() +{ + if (DataToRecoverVSProject == nullptr) + { + LOG.Add("FENativeScriptProject::RegenerateVSProject: DataToRecoverVSProject is nullptr", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + std::vector AssetPackageContent = DataToRecoverVSProject->GetEntryList(); + if (AssetPackageContent.empty()) + { + LOG.Add("FENativeScriptProject::RegenerateVSProject: Asset package is empty", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (Parent == nullptr) + { + LOG.Add("FENativeScriptProject::RegenerateVSProject: Parent is nullptr", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (!EnsureVSProjectDirectoryIsIntact()) + { + LOG.Add("FENativeScriptProject::RegenerateVSProject: Error ensuring VS project directory is intact", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + std::vector SourceFileFullPathList; + for (size_t i = 0; i < AssetPackageContent.size(); i++) + { + DataToRecoverVSProject->ExportAssetToFile(AssetPackageContent[i].ID, VSProjectDirectory + AssetPackageContent[i].Name); + SourceFileFullPathList.push_back(VSProjectDirectory + AssetPackageContent[i].Name); + } + + if (!InitializeProject(SourceFileFullPathList)) + return false; + + if (!ConfigureAndBuildCMake()) + return false; + + return true; +} + +bool FENativeScriptProject::InitializeProject(std::vector SourceFileFullPathList) +{ + if (Parent == nullptr) + { + LOG.Add("FENativeScriptProject::InitializeProject: Parent is nullptr", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (VSProjectDirectory.empty()) + { + LOG.Add("FENativeScriptProject::InitializeProject: Project path is empty", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (SourceFileFullPathList.empty()) + { + LOG.Add("FENativeScriptProject::InitializeProject: source file list are empty", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (!SourceFileFullPathList.empty()) + { + for (size_t i = 0; i < SourceFileFullPathList.size(); i++) + { + if (!FILE_SYSTEM.DoesFileExist(SourceFileFullPathList[i])) + { + LOG.Add("FENativeScriptProject::InitializeProject: Source file " + SourceFileFullPathList[i] + " does not exist", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + } + } + + // Create all needed folders. + std::vector FoldersToCreate = { "SubSystems/", "SubSystems/FocalEngine", "BuildManagement/", "SubSystems/FocalEngine/Resources/", "SubSystems/FocalEngine/Resources/UserScriptsData/" }; + for (size_t i = 0; i < FoldersToCreate.size(); i++) + { + if (!FILE_SYSTEM.CreateDirectory(VSProjectDirectory + FoldersToCreate[i])) + { + LOG.Add("FENativeScriptProject::InitializeProject: Error creating " + FoldersToCreate[i] + " directory", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + + // Place required files in the destination directory. + std::string EnginePath = FILE_SYSTEM.GetCurrentWorkingPath() + "/" + std::string(ENGINE_FOLDER); + std::vector> FilesToCopy; + FilesToCopy.push_back({ EnginePath + "/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/EnsureBuildCompletion.cmake", VSProjectDirectory + "BuildManagement/EnsureBuildCompletion.cmake" }); + FilesToCopy.push_back({ EnginePath + "/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/DebugBuildActions.cmake", VSProjectDirectory + "BuildManagement/DebugBuildActions.cmake" }); + FilesToCopy.push_back({ EnginePath + "/Resources/UserScriptsData/NativeScriptProjectData/BuildManagement/ReleaseBuildActions.cmake", VSProjectDirectory + "BuildManagement/ReleaseBuildActions.cmake" }); + FilesToCopy.push_back({ EnginePath + "/Resources/UserScriptsData/NativeScriptProjectData/CMakeLists_Template.txt", VSProjectDirectory + "CMakeLists.txt" }); + // We need to copy only one .cpp file. + FilesToCopy.push_back({ EnginePath + "/Resources/UserScriptsData/FENativeScriptConnector.cpp", VSProjectDirectory + "SubSystems/FocalEngine/Resources/UserScriptsData/FENativeScriptConnector.cpp" }); + + for (size_t i = 0; i < SourceFileFullPathList.size(); i++) + { + FilesToCopy.push_back({ SourceFileFullPathList[i], VSProjectDirectory + FILE_SYSTEM.GetFileName(SourceFileFullPathList[i]) }); + if (FilesToCopy.back().first == FilesToCopy.back().second) + FilesToCopy.pop_back(); + } + + for (size_t i = 0; i < FilesToCopy.size(); i++) + { + if (!FILE_SYSTEM.CopyFile(FilesToCopy[i].first, FilesToCopy[i].second)) + { + LOG.Add("FENativeScriptProject::InitializeProject: Error copying file " + FilesToCopy[i].first + " to " + FilesToCopy[i].second, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + + SourceFileList.clear(); + for (size_t i = 0; i < SourceFileFullPathList.size(); i++) + { + SourceFileList.push_back(FILE_SYSTEM.GetFileName(SourceFileFullPathList[i])); + } + + if (!InitializeCMakeFileAndScriptFiles(SourceFileFullPathList)) + { + LOG.Add("FENativeScriptProject::InitializeProject: Error initializing CMakeLists.txt", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (!RESOURCE_MANAGER.CopyEngineFiles(true, false, true, VSProjectDirectory)) + { + LOG.Add("FENativeScriptProject::InitializeProject: Error copying engine files in user native script project", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + SetFileTracking(); + return true; +} + +bool FENativeScriptProject::InitializeCMakeFileAndScriptFiles(std::vector SourceFileFullPathList) +{ + if (VSProjectDirectory.empty()) + { + LOG.Add("FENativeScriptProject::InitializeCMakeFileAndScriptFiles: path is empty", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (Parent == nullptr) + { + LOG.Add("FENativeScriptProject::InitializeCMakeFileAndScriptFiles: Parent is nullptr", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + std::string CMakeFilePath = VSProjectDirectory + "CMakeLists.txt"; + + std::vector Instructions; + FEFileSystem::TextReplacementRule CurrentInstruction; + CurrentInstruction.ContextPattern = "set(PROJECT_NAME PLACE_HOLDER)"; + CurrentInstruction.TargetText = "PLACE_HOLDER"; + CurrentInstruction.ReplacementText = GetVSProjectName(); + Instructions.push_back(CurrentInstruction); + + std::string SourceFilesList = "file(GLOB Main_SRC\n"; + for (size_t i = 0; i < SourceFileFullPathList.size(); i++) + { + SourceFilesList += "\t\"" + FILE_SYSTEM.GetFileName(SourceFileFullPathList[i]) + "\"\n"; + } + SourceFilesList += ")"; + + CurrentInstruction.ContextPattern = "file(GLOB Main_SRC PLACE_HOLDER)"; + CurrentInstruction.TargetText = "file(GLOB Main_SRC PLACE_HOLDER)"; + CurrentInstruction.ReplacementText = SourceFilesList; + Instructions.push_back(CurrentInstruction); + + if (!FILE_SYSTEM.PerformTextReplacements(CMakeFilePath, Instructions)) + { + LOG.Add("FENativeScriptProject::InitializeCMakeFileAndScriptFiles: Error initializing CMakeLists.txt", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + Instructions.clear(); + + CurrentInstruction.ContextPattern = "--target PLACE_HOLDER --config Release"; + CurrentInstruction.TargetText = "PLACE_HOLDER"; + CurrentInstruction.ReplacementText = GetVSProjectName(); + Instructions.push_back(CurrentInstruction); + + std::string DebugBuildActionsCMake = VSProjectDirectory + "BuildManagement/DebugBuildActions.cmake"; + if (!FILE_SYSTEM.PerformTextReplacements(DebugBuildActionsCMake, Instructions)) + { + LOG.Add("FENativeScriptProject::InitializeCMakeFileAndScriptFiles: Error initializing BuildManagement/DebugBuildActions.cmake", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + Instructions.clear(); + + CurrentInstruction.ContextPattern = "--target PLACE_HOLDER --config Debug"; + CurrentInstruction.TargetText = "PLACE_HOLDER"; + CurrentInstruction.ReplacementText = GetVSProjectName(); + Instructions.push_back(CurrentInstruction); + + std::string ReleaseBuildActionsCMake = VSProjectDirectory + "BuildManagement/ReleaseBuildActions.cmake"; + if (!FILE_SYSTEM.PerformTextReplacements(ReleaseBuildActionsCMake, Instructions)) + { + LOG.Add("FENativeScriptProject::InitializeCMakeFileAndScriptFiles: Error initializing BuildManagement/ReleaseBuildActions.cmake", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + Instructions.clear(); + + return true; +} + +bool FENativeScriptProject::ConfigureAndBuildCMake() +{ + if (VSProjectDirectory.empty()) + { + LOG.Add("FENativeScriptProject::ConfigureAndBuildCMake: VSProjectDirectory is empty", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + std::string Generator = "Visual Studio 17 2022"; + + // CMake configure command. + std::string ConfigureCommand = "cmake -S \"" + VSProjectDirectory + "\" -B \"" + VSProjectDirectory + "\" -G \"" + Generator + "\""; + + // Execute CMake configure command. + int ConfigureResult = std::system(ConfigureCommand.c_str()); + if (ConfigureResult != 0) + { + LOG.Add("FENativeScriptProject::ConfigureAndBuildCMake: Error running CMake configure command", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + // Construct the CMake build command + std::string BuildCommand = "cmake --build \"" + VSProjectDirectory + "\" --config Debug"; + + // Execute CMake build command + int BuildResult = std::system(BuildCommand.c_str()); + if (BuildResult != 0) + { + LOG.Add("FENativeScriptProject::ConfigureAndBuildCMake: Error running CMake build command", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + return true; +} + +bool FENativeScriptProject::RunVSProject() +{ + if (VSProjectDirectory.empty()) + { + LOG.Add("FENativeScriptProject::RunProjectVSSolution: VSProjectDirectory is empty", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + std::string SolutionPath = VSProjectDirectory + GetVSProjectName() + ".sln"; + + // Use ShellExecute to open the solution file + HINSTANCE Result = ShellExecuteA( + NULL, + "open", + SolutionPath.c_str(), + NULL, + NULL, + SW_SHOWNORMAL + ); + + // Check if ShellExecute was successful + if ((INT_PTR)Result <= 32) + { + LOG.Add("FENativeScriptProject::RunProjectVSSolution: Error running solution", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + return true; +} + +void FENativeScriptProject::SetFileTracking() +{ + DebugDllFileData.Path = VSProjectDirectory + "Debug/" + GetVSProjectName() + ".dll"; + DebugDllFileData.WriteTime = FILE_SYSTEM.GetFileLastWriteTime(DebugDllFileData.Path); + + DebugPdbFileData.Path = VSProjectDirectory + "Debug/" + GetVSProjectName() + ".pdb"; + DebugPdbFileData.WriteTime = FILE_SYSTEM.GetFileLastWriteTime(DebugPdbFileData.Path); + + ReleaseDllFileData.Path = VSProjectDirectory + "Release/" + GetVSProjectName() + ".dll"; + ReleaseDllFileData.WriteTime = FILE_SYSTEM.GetFileLastWriteTime(ReleaseDllFileData.Path); +} + +bool FENativeScriptProject::GenerateScriptFilesFromTemplate(std::string ScriptName) +{ + if (!FILE_SYSTEM.DoesDirectoryExist(VSProjectDirectory)) + { + LOG.Add("FENativeScriptProject::GenerateScriptFilesFromTemplate: VSProjectDirectory does not exist", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (ScriptName.empty()) + { + LOG.Add("FENativeScriptProject::GenerateScriptFilesFromTemplate: Script name is empty", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + std::string EnginePath = FILE_SYSTEM.GetCurrentWorkingPath() + "/" + std::string(ENGINE_FOLDER); + std::string TemplateHeaderFilePath = EnginePath + "/Resources/UserScriptsData/NativeScriptTemplate.h"; + std::string TemplateCPPFilePath = EnginePath + "/Resources/UserScriptsData/NativeScriptTemplate.cpp"; + + if (!FILE_SYSTEM.CopyFile(TemplateHeaderFilePath, VSProjectDirectory + ScriptName + ".h")) + { + LOG.Add("FENativeScriptProject::GenerateScriptFilesFromTemplate: Error copying file " + TemplateHeaderFilePath + " to " + VSProjectDirectory + ScriptName + ".h", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (!FILE_SYSTEM.CopyFile(TemplateCPPFilePath, VSProjectDirectory + ScriptName + ".cpp")) + { + LOG.Add("FENativeScriptProject::GenerateScriptFilesFromTemplate: Error copying file " + TemplateCPPFilePath + " to " + VSProjectDirectory + ScriptName + ".cpp", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + std::vector Instructions; + FEFileSystem::TextReplacementRule CurrentInstruction; + CurrentInstruction.ContextPattern = "class PLACE_HOLDER : public FENativeScriptCore"; + CurrentInstruction.TargetText = "PLACE_HOLDER"; + CurrentInstruction.ReplacementText = ScriptName; + Instructions.push_back(CurrentInstruction); + + CurrentInstruction.ContextPattern = "REGISTER_SCRIPT(PLACE_HOLDER)"; + CurrentInstruction.TargetText = "PLACE_HOLDER"; + CurrentInstruction.ReplacementText = ScriptName; + Instructions.push_back(CurrentInstruction); + + CurrentInstruction.ContextPattern = "REGISTER_SCRIPT_FIELD(PLACE_HOLDER, int, ExampleVariable)"; + CurrentInstruction.TargetText = "PLACE_HOLDER"; + CurrentInstruction.ReplacementText = ScriptName; + Instructions.push_back(CurrentInstruction); + + CurrentInstruction.ContextPattern = "SET_MODULE_ID(\"PLACE_HOLDER\");"; + CurrentInstruction.TargetText = "PLACE_HOLDER"; + CurrentInstruction.ReplacementText = Parent->GetObjectID(); + Instructions.push_back(CurrentInstruction); + + std::string ScriptHeaderFilePath = VSProjectDirectory + ScriptName + ".h"; + if (!FILE_SYSTEM.PerformTextReplacements(ScriptHeaderFilePath, Instructions)) + { + LOG.Add("FENativeScriptProject::GenerateScriptFilesFromTemplate: Error initializing " + ScriptName + ".h", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + Instructions.clear(); + + CurrentInstruction.ContextPattern = "#include \"NativeScriptTemplate.h\""; + CurrentInstruction.TargetText = "NativeScriptTemplate"; + CurrentInstruction.ReplacementText = ScriptName; + Instructions.push_back(CurrentInstruction); + + CurrentInstruction.ContextPattern = "void PLACE_HOLDER::Awake()"; + CurrentInstruction.TargetText = "PLACE_HOLDER"; + CurrentInstruction.ReplacementText = ScriptName; + Instructions.push_back(CurrentInstruction); + + CurrentInstruction.ContextPattern = "void PLACE_HOLDER::OnDestroy()"; + CurrentInstruction.TargetText = "PLACE_HOLDER"; + CurrentInstruction.ReplacementText = ScriptName; + Instructions.push_back(CurrentInstruction); + + CurrentInstruction.ContextPattern = "void PLACE_HOLDER::OnUpdate(double DeltaTime)"; + CurrentInstruction.TargetText = "PLACE_HOLDER"; + CurrentInstruction.ReplacementText = ScriptName; + Instructions.push_back(CurrentInstruction); + + std::string ScriptSourceFilePath = VSProjectDirectory + ScriptName + ".cpp"; + if (!FILE_SYSTEM.PerformTextReplacements(ScriptSourceFilePath, Instructions)) + { + LOG.Add("FENativeScriptProject::GenerateScriptFilesFromTemplate: Error initializing " + ScriptName + ".cpp", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + Instructions.clear(); + + return true; +} + +bool FENativeScriptProject::CreateNewVSProject(std::string FirstScriptName) +{ + if (FirstScriptName.empty()) + { + LOG.Add("FENativeScriptProject::GenerateNewVSProject: First script name is empty", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + if (Parent == nullptr) + { + LOG.Add("FENativeScriptProject::GenerateNewVSProject: Parent is nullptr", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return false; + } + + // Replace all spaces with underscores. + for (size_t i = 0; i < FirstScriptName.size(); i++) + { + if (FirstScriptName[i] == ' ') + FirstScriptName[i] = '_'; + } + + if (!EnsureVSProjectDirectoryIsIntact()) + { + LOG.Add("FENativeScriptProject::CreateNewVSProject: Error ensuring VS project directory is intact", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (!GenerateScriptFilesFromTemplate(FirstScriptName)) + { + LOG.Add("FENativeScriptProject::GenerateNewVSProject: Error generating script files from template", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (!InitializeProject({ VSProjectDirectory + FirstScriptName + ".h", VSProjectDirectory + FirstScriptName + ".cpp" })) + return false; + + if (!ConfigureAndBuildCMake()) + return false; + + return RunVSProject(); +} + +bool FENativeScriptProject::IsFileChanged(const TrackedFileData& FileData) +{ + uint64_t NewWriteTime = FILE_SYSTEM.GetFileLastWriteTime(FileData.Path); + if (NewWriteTime != FileData.WriteTime && NewWriteTime != 0) + { + uint64_t Differece = NewWriteTime >= FileData.WriteTime ? NewWriteTime - FileData.WriteTime : FileData.WriteTime - NewWriteTime; + if (Differece > DifferenceThreshold) + return true; + } + + return false; +} + +void FENativeScriptProject::UpdateTrackedFileWriteTime(TrackedFileData& FileData) +{ + uint64_t NewTimeStamp = FILE_SYSTEM.GetFileLastWriteTime(FileData.Path); + + if (NewTimeStamp > FileData.WriteTime) + FileData.WriteTime = NewTimeStamp; +} + +bool FENativeScriptProject::Update() +{ + if (!IsVSProjectFolderValidAndIntact()) + return false; + + bool CheckForReload = false; + if (IsFileChanged(DebugDllFileData) && IsFileChanged(DebugPdbFileData) || IsFileChanged(ReleaseDllFileData)) + CheckForReload = true; + + if (!CheckForReload) + return false; + + // Waiting for external build system to finish. + FILE_SYSTEM.WaitForFileAccess(VSProjectDirectory + "BuildManagement/Force_Build_Finished.txt", 1000); + + if (FILE_SYSTEM.DoesFileExist(VSProjectDirectory + "BuildManagement/Force_Build_Finished.txt")) + { + FILE_SYSTEM.DeleteFile(VSProjectDirectory + "BuildManagement/Force_Build_Finished.txt"); + } + else + { + LOG.Add("FENativeScriptProject::Update: Force_Build_Finished.txt does not exist, script reload aborted.", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + + UpdateTrackedFileWriteTime(DebugDllFileData); + UpdateTrackedFileWriteTime(DebugPdbFileData); + UpdateTrackedFileWriteTime(ReleaseDllFileData); + + return false; + } + + // Waiting for all files to be accessible. + if (!FILE_SYSTEM.WaitForFileAccess(DebugDllFileData.Path, 2000)) + { + LOG.Add("FENativeScriptProject::Update: File " + DebugDllFileData.Path + "does not exist, script reload aborted.", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + + UpdateTrackedFileWriteTime(DebugDllFileData); + UpdateTrackedFileWriteTime(DebugPdbFileData); + UpdateTrackedFileWriteTime(ReleaseDllFileData); + + return false; + } + + if (!FILE_SYSTEM.WaitForFileAccess(DebugPdbFileData.Path, 2000)) + { + LOG.Add("FENativeScriptProject::Update: File " + DebugPdbFileData.Path + "does not exist, script reload aborted.", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + + UpdateTrackedFileWriteTime(DebugDllFileData); + UpdateTrackedFileWriteTime(DebugPdbFileData); + UpdateTrackedFileWriteTime(ReleaseDllFileData); + + return false; + } + + if (!FILE_SYSTEM.WaitForFileAccess(ReleaseDllFileData.Path, 2000)) + { + LOG.Add("FENativeScriptProject::Update: File " + ReleaseDllFileData.Path + "does not exist, script reload aborted.", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + + UpdateTrackedFileWriteTime(DebugDllFileData); + UpdateTrackedFileWriteTime(DebugPdbFileData); + UpdateTrackedFileWriteTime(ReleaseDllFileData); + + return false; + } + + bool bResult = UpdateParentScriptModule(); + + // In some cases we would want to save such module right away. + // It is needed to ensure that the module is saved before the next frame. + // TO-DO: It is a hack and should be replaced with a proper solution. + RESOURCE_MANAGER.SaveFENativeScriptModule(Parent, VSProjectDirectory + Parent->GetObjectID() + ".nativescriptmodule"); + + UpdateTrackedFileWriteTime(DebugDllFileData); + UpdateTrackedFileWriteTime(DebugPdbFileData); + UpdateTrackedFileWriteTime(ReleaseDllFileData); + + return bResult; +} + +std::vector FENativeScriptProject::GetSourceFileList() +{ + return SourceFileList; +} + +bool FENativeScriptProject::ExtractSourceFilesTo(std::string DestinationDirectory) +{ + if (DestinationDirectory.empty()) + { + LOG.Add("FENativeScriptProject::ExtractSourceFilesTo: Destination directory is empty", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (!FILE_SYSTEM.DoesDirectoryExist(DestinationDirectory)) + { + LOG.Add("FENativeScriptProject::ExtractSourceFilesTo: Destination directory does not exist", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + std::vector AssetPackageContent = DataToRecoverVSProject->GetEntryList(); + if (AssetPackageContent.empty()) + { + LOG.Add("FENativeScriptProject::ExtractSourceFilesTo: Asset package is empty", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + std::vector SourceFileFullPathList; + for (size_t i = 0; i < AssetPackageContent.size(); i++) + { + if (!DataToRecoverVSProject->ExportAssetToFile(AssetPackageContent[i].ID, DestinationDirectory + AssetPackageContent[i].Name)) + { + LOG.Add("FENativeScriptProject::ExtractSourceFilesTo: Error exporting asset " + AssetPackageContent[i].Name, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptProject.h b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptProject.h new file mode 100644 index 0000000..d79c082 --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptProject.h @@ -0,0 +1,75 @@ +#pragma once +#include "FENativeScriptSystem.h" +using namespace FocalEngine; + +namespace FocalEngine +{ + struct TrackedFileData + { + std::string Path; + uint64_t WriteTime; + }; + + class FENativeScriptProject + { + friend class FENativeScriptModule; + friend class FEResourceManager; + + FENativeScriptModule* Parent = nullptr; + + std::string WorkingDirectory = ""; + std::string VSProjectDirectory = ""; + bool EnsureVSProjectDirectoryIsIntact(); + + std::vector SourceFileList; + FEAssetPackage* DataToRecoverVSProject = nullptr; + + TrackedFileData DebugDllFileData; + TrackedFileData DebugPdbFileData; + TrackedFileData ReleaseDllFileData; + + const uint64_t DifferenceThreshold = 1'000'000; + + FENativeScriptModule* LastGeneratedScriptModule = nullptr; + FENativeScriptModule* ModuleThatIsBeingEdited = nullptr; + + bool InitializeProject(std::vector SourceFileFullPathList); + bool ConfigureAndBuildCMake(); + + bool InitializeCMakeFileAndScriptFiles(std::vector SourceFileFullPathList); + + void SetFileTracking(); + bool GenerateScriptFilesFromTemplate(std::string ScriptName); + + bool IsFileChanged(const TrackedFileData& FileData); + void UpdateTrackedFileWriteTime(TrackedFileData& FileData); + + bool UpdateParentScriptModule(); + public: + FENativeScriptProject(); + ~FENativeScriptProject(); + + void SetWorkingDirectory(std::string NewValue); + std::string GetWorkingDirectory(); + std::string GetVSProjectDirectory(); + + // Space in project name is not allowed + std::string GetVSProjectName(); + + bool HasRecoverableVSProjectData(); + bool CreateNewVSProject(std::string FirstScriptName); + + bool IsVSProjectFolderValidAndIntact(); + bool RecreateVSProject(); + + bool RunVSProject(); + + bool Initialize(FEAssetPackage* Data); + void UpdateDataToRecoverVSProject(); + + bool Update(); + + std::vector GetSourceFileList(); + bool ExtractSourceFilesTo(std::string DestinationDirectory); + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.cpp b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.cpp new file mode 100644 index 0000000..1c9f972 --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.cpp @@ -0,0 +1,1244 @@ +#include "FENativeScriptSystem.h" +#ifndef FOCAL_ENGINE_SHARED +#include "../../../../Resources/UserScriptsData/FENativeScriptConnector.h" +#endif +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetNativeScriptSystem() +{ + return FENativeScriptSystem::GetInstancePointer(); +} +#endif + +FENativeScriptSystem::FENativeScriptSystem() +{ + RegisterOnComponentCallbacks(); + COMPONENTS_TOOL.RegisterComponentToJsonFunction(NativeScriptComponentToJson); + COMPONENTS_TOOL.RegisterComponentFromJsonFunction(NativeScriptComponentFromJson); + COMPONENTS_TOOL.RegisterComponentDuplicateFunction(DuplicateNativeScriptComponent); + + // We should clean folder with extracted native scripts. + std::string ExtractedFolderPath = FILE_SYSTEM.GetCurrentWorkingPath() + "/ExtractedNativeScripts/"; + if (FILE_SYSTEM.DoesDirectoryExist(ExtractedFolderPath)) + FILE_SYSTEM.DeleteDirectory(ExtractedFolderPath); + + // Activate all standard script modules. + // After engine is initialized, we should activate the modules. + std::vector ModulesIDsToActivate = RESOURCE_MANAGER.GetNativeScriptModuleIDList(); + for (size_t i = 0; i < ModulesIDsToActivate.size(); i++) + { + ActivateNativeScriptModule(ModulesIDsToActivate[i]); + } + + FENativeScriptModule* ModuleWithCameraScripts = RESOURCE_MANAGER.GetNativeScriptModule("2B7956623302254F620A675F"); + if (ModuleWithCameraScripts == nullptr) + { + LOG.Add("can't find module with camera scripts in FEResourceManager::LoadStandardPrefabs", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + + // Free camera + FEPrefab* FreeCameraPrefab = RESOURCE_MANAGER.CreatePrefab("Free camera prefab", "4575527C773848040760656F"); + FreeCameraPrefab->SetTag(ENGINE_RESOURCE_TAG); + + FEScene* PrefabScene = FreeCameraPrefab->GetScene(); + + FEEntity* CameraEntity = PrefabScene->CreateEntity("Free camera"); + CameraEntity->AddComponent(); + CameraEntity->AddComponent(); + InitializeScriptComponent(CameraEntity, ModuleWithCameraScripts->GetObjectID(), "FreeCameraController"); + + // Model view camera + FEPrefab* ModelViewCameraPrefab = RESOURCE_MANAGER.CreatePrefab("Model view camera prefab", "14745A482D1B2C328C268027"); + ModelViewCameraPrefab->SetTag(ENGINE_RESOURCE_TAG); + + PrefabScene = ModelViewCameraPrefab->GetScene(); + + CameraEntity = PrefabScene->CreateEntity("Model view camera"); + CameraEntity->AddComponent(); + CameraEntity->AddComponent(); + InitializeScriptComponent(CameraEntity, ModuleWithCameraScripts->GetObjectID(), "ModelViewCameraController"); +} + +void FENativeScriptSystem::RegisterOnComponentCallbacks() +{ + SCENE_MANAGER.RegisterOnComponentConstructCallback(OnMyComponentAdded); + SCENE_MANAGER.RegisterOnComponentDestroyCallback(OnMyComponentDestroy); +} + +void FENativeScriptSystem::OnMyComponentAdded(FEEntity* Entity) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FENativeScriptComponent& NativeScriptComponent = Entity->GetComponent(); +} + +void FENativeScriptSystem::OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FENativeScriptComponent& NativeScriptComponent = Entity->GetComponent(); +} + +FENativeScriptSystem::~FENativeScriptSystem() {}; + +void FENativeScriptSystem::Update(double DeltaTime) +{ + std::vector ActiveGameModeScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active | FESceneFlag::GameMode); + for (FEScene* Scene : ActiveGameModeScenes) + { + std::vector Entities = Scene->GetEntityListWithComponent(); + + for (size_t i = 0; i < Entities.size(); i++) + { + FENativeScriptComponent& NativeScriptComponent = Entities[i]->GetComponent(); + if (NativeScriptComponent.IsInitialized()) + NativeScriptComponent.OnUpdate(DeltaTime); + } + } + + std::vector ActiveEditorScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active | FESceneFlag::EditorMode); + for (FEScene* Scene : ActiveEditorScenes) + { + std::vector Entities = Scene->GetEntityListWithComponent(); + + for (size_t i = 0; i < Entities.size(); i++) + { + FENativeScriptComponent& NativeScriptComponent = Entities[i]->GetComponent(); + if (NativeScriptComponent.IsInitialized() && NativeScriptComponent.ScriptData->bRunInEditor) + NativeScriptComponent.OnUpdate(DeltaTime); + } + } +} + +void FENativeScriptSystem::CopyVariableValuesInternal(FENativeScriptComponent* SourceComponent, FENativeScriptComponent* TargetComponent) +{ + if (SourceComponent == nullptr) + { + LOG.Add("FENativeScriptSystem::CopyVariableValuesInternal failed to copy variable values from null component.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + if (SourceComponent->CoreInstance == nullptr) + { + LOG.Add("FENativeScriptSystem::CopyVariableValuesInternal failed to copy variable values from component with null core instance.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + if (TargetComponent == nullptr) + { + LOG.Add("FENativeScriptSystem::CopyVariableValuesInternal failed to copy variable values to null component.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + if (TargetComponent->CoreInstance == nullptr) + { + LOG.Add("FENativeScriptSystem::CopyVariableValuesInternal failed to copy variable values to component with null core instance.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + std::unordered_map SourceVariables = GetVariablesRegistry(SourceComponent->CoreInstance->ParentEntity); + std::unordered_map TargetVariables = GetVariablesRegistry(TargetComponent->CoreInstance->ParentEntity); + + auto VariableIterator = SourceVariables.begin(); + while (VariableIterator != SourceVariables.end()) + { + std::string VariableName = VariableIterator->first; + FEScriptVariableInfo VariableInfo = VariableIterator->second; + + if (TargetVariables.find(VariableName) == TargetVariables.end()) + continue; + + FEScriptVariableInfo TargetVariableInfo = TargetVariables[VariableName]; + if (VariableInfo.Type != TargetVariableInfo.Type) + continue; + + std::any VariableValue = VariableInfo.Getter(SourceComponent->GetCoreInstance()); + TargetVariableInfo.Setter(TargetComponent->GetCoreInstance(), VariableValue); + + VariableIterator++; + } +} + +void FENativeScriptSystem::DuplicateNativeScriptComponent(FEEntity* SourceEntity, FEEntity* TargetEntity) +{ + if (SourceEntity == nullptr) + { + LOG.Add("FENativeScriptSystem::DuplicateNativeScriptComponent failed to duplicate script component from null entity.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + if (TargetEntity == nullptr) + { + LOG.Add("FENativeScriptSystem::DuplicateNativeScriptComponent failed to duplicate script component to null entity.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + if (!SourceEntity->HasComponent()) + { + LOG.Add("FENativeScriptSystem::DuplicateNativeScriptComponent failed to duplicate script component from entity without FENativeScriptComponent.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + if (TargetEntity->HasComponent()) + { + LOG.Add("FENativeScriptSystem::DuplicateNativeScriptComponent failed to duplicate script component to entity with FENativeScriptComponent.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + FENativeScriptComponent& SourceComponent = SourceEntity->GetComponent(); + TargetEntity->AddComponent(); + NATIVE_SCRIPT_SYSTEM.InitializeComponentInternal(TargetEntity, TargetEntity->GetComponent(), SourceComponent.GetModuleID(), *SourceComponent.ScriptData); + NATIVE_SCRIPT_SYSTEM.CopyVariableValuesInternal(&SourceComponent, &TargetEntity->GetComponent()); +} + +void FENativeScriptSystem::SaveVariableToJSON(Json::Value& Root, FEScriptVariableInfo& VariableInfo, FENativeScriptCore* Core) +{ + if (Core == nullptr) + { + LOG.Add("FENativeScriptSystem::SaveVariableToJSON Core is nullptr", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return; + } + + std::string VariableName = VariableInfo.Name; + Root[VariableName]["Name"] = VariableName; + Root[VariableName]["Type"] = VariableInfo.Type; + + if (VariableInfo.Type.find("vector<") != std::string::npos || VariableInfo.Type.find("std::vector<") != std::string::npos) + { + if (VariableInfo.Type.find("float>") != std::string::npos) SaveArrayVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type.find("int>") != std::string::npos) SaveArrayVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type.find("bool>") != std::string::npos) SaveArrayVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type.find("std::string>") != std::string::npos) SaveArrayVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type.find("glm::vec2>") != std::string::npos) SaveArrayVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type.find("glm::vec3>") != std::string::npos) SaveArrayVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type.find("glm::vec4>") != std::string::npos) SaveArrayVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + } + else + { + if (VariableInfo.Type == "int") SaveVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type == "float") SaveVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type == "double") SaveVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type == "bool") SaveVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type == "glm::vec2") SaveVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type == "glm::vec3") SaveVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type == "glm::vec4") SaveVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type == "glm::quat") SaveVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type == "string") SaveVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + else if (VariableInfo.Type == "FEPrefab*") SaveVariableTTypeToJSON(Root[VariableName]["Value"], VariableInfo.Getter(Core)); + } +} + +Json::Value FENativeScriptSystem::NativeScriptComponentToJson(FEEntity* Entity) +{ + Json::Value Root; + FENativeScriptComponent& NativeScriptComponent = Entity->GetComponent(); + + if (NativeScriptComponent.IsFailedToLoad()) + { + Root = NativeScriptComponent.FailedToLoadData->RawData; + return Root; + } + + Root["ModuleID"] = NativeScriptComponent.ModuleID; + Root["Name"] = NativeScriptComponent.GetScriptData()->Name; + + Json::Value VariablesRoot; + std::unordered_map Variables = NATIVE_SCRIPT_SYSTEM.GetVariablesRegistry(Entity); + + auto VariableIterator = Variables.begin(); + while (VariableIterator != Variables.end()) + { + NATIVE_SCRIPT_SYSTEM.SaveVariableToJSON(VariablesRoot, VariableIterator->second, NativeScriptComponent.GetCoreInstance()); + VariableIterator++; + } + Root["Variables"] = VariablesRoot; + + return Root; +} + +void FENativeScriptSystem::LoadVariableFromJSON(Json::Value& Root, FEScriptVariableInfo& VariableInfo, FENativeScriptCore* Core) +{ + if (Core == nullptr) + { + LOG.Add("FENativeScriptSystem::LoadVariableFromJSON Core is nullptr", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return; + } + + std::string VariableName = VariableInfo.Name; + if (VariableInfo.Type != Root["Type"].asString()) + { + LOG.Add("FENativeScriptSystem::LoadVariableFromJSON failed to load variable with name: " + VariableName + " because types are different.", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return; + } + + if (VariableInfo.Type.find("vector<") != std::string::npos || VariableInfo.Type.find("std::vector<") != std::string::npos) + { + if (VariableInfo.Type.find("float>") != std::string::npos) VariableInfo.Setter(Core, LoadArrayVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type.find("int>") != std::string::npos) VariableInfo.Setter(Core, LoadArrayVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type.find("bool>") != std::string::npos) VariableInfo.Setter(Core, LoadArrayVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type.find("std::string>") != std::string::npos) VariableInfo.Setter(Core, LoadArrayVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type.find("glm::vec2>") != std::string::npos) VariableInfo.Setter(Core, LoadArrayVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type.find("glm::vec3>") != std::string::npos) VariableInfo.Setter(Core, LoadArrayVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type.find("glm::vec4>") != std::string::npos) VariableInfo.Setter(Core, LoadArrayVariableTTypeToJSON(Root["Value"])); + } + else + { + if (VariableInfo.Type == "int") VariableInfo.Setter(Core, LoadVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type == "float") VariableInfo.Setter(Core, LoadVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type == "double") VariableInfo.Setter(Core, LoadVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type == "bool") VariableInfo.Setter(Core, LoadVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type == "glm::vec2") VariableInfo.Setter(Core, LoadVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type == "glm::vec3") VariableInfo.Setter(Core, LoadVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type == "glm::vec4") VariableInfo.Setter(Core, LoadVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type == "glm::quat") VariableInfo.Setter(Core, LoadVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type == "string") VariableInfo.Setter(Core, LoadVariableTTypeToJSON(Root["Value"])); + else if (VariableInfo.Type == "FEPrefab*") VariableInfo.Setter(Core, LoadVariableTTypeToJSON(Root["Value"])); + } +} + +void FENativeScriptSystem::NativeScriptComponentFromJson(FEEntity* Entity, Json::Value Root) +{ + if (Entity == nullptr) + { + LOG.Add("FENativeScriptSystem::NativeScriptComponentFromJson Entity is nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + + std::string ModuleID = Root["ModuleID"].asString(); + if (ModuleID.empty()) + { + LOG.Add("FENativeScriptSystem::NativeScriptComponentFromJson failed to get ModuleID from JSON.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + std::string ScriptName = Root["Name"].asString(); + if (ScriptName.empty()) + { + LOG.Add("FENativeScriptSystem::NativeScriptComponentFromJson failed to get ScriptName from JSON.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + // After this point if we fail to initialize script component, we should add failed to load data to entity. + Entity->AddComponent(); + if (!NATIVE_SCRIPT_SYSTEM.InitializeScriptComponent(Entity, ModuleID, ScriptName)) + { + LOG.Add("FENativeScriptSystem::NativeScriptComponentFromJson failed to initialize script component.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + //Entity->RemoveComponent(); + NATIVE_SCRIPT_SYSTEM.AddFailedToLoadData(Entity, ModuleID, Root); + return; + } + + FENativeScriptComponent& NativeScriptComponent = Entity->GetComponent(); + std::unordered_map Variables = NATIVE_SCRIPT_SYSTEM.GetVariablesRegistry(Entity); + + auto VariableIterator = Variables.begin(); + while (VariableIterator != Variables.end()) + { + std::string VariableName = VariableIterator->first; + Json::Value VariableRoot = Root["Variables"][VariableName]; + if (VariableRoot.isNull()) + { + VariableIterator++; + continue; + } + + NATIVE_SCRIPT_SYSTEM.LoadVariableFromJSON(VariableRoot, VariableIterator->second, NativeScriptComponent.GetCoreInstance()); + VariableIterator++; + } +} + +bool FENativeScriptSystem::InitializeComponentInternal(FEEntity* Entity, FENativeScriptComponent& NativeScriptComponent, std::string ActiveModuleID, FEScriptData& ScriptData) +{ + if (Entity == nullptr) + { + LOG.Add("FENativeScriptSystem::InitializeComponentInternal failed to add script component to null entity.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (ActiveModuleID.empty()) + { + LOG.Add("FENativeScriptSystem::InitializeComponentInternal failed to add script component to entity with empty ModuleID.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + FEScene* Scene = Entity->GetParentScene(); + if (Scene == nullptr) + { + LOG.Add("FENativeScriptSystem::InitializeComponentInternal failed to add script component to entity without parent scene.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + NativeScriptComponent.CoreInstance = ScriptData.ConstructorFunction(); + NativeScriptComponent.ModuleID = ActiveModuleID; + NativeScriptComponent.CoreInstance->ParentEntity = Entity; + NativeScriptComponent.ScriptData = &ScriptData; + + if (Scene->HasFlag(FESceneFlag::EditorMode)) + { + if (ScriptData.bRunInEditor) + NativeScriptComponent.Awake(); + } + else if (Scene->HasFlag(FESceneFlag::GameMode)) + { + NativeScriptComponent.Awake(); + } + + return true; +} + +void FENativeScriptSystem::AddFailedToLoadData(FEEntity* Entity, std::string ModuleID, Json::Value RawData) +{ + if (Entity == nullptr) + { + LOG.Add("FENativeScriptSystem::AddFailedToLoadData failed to add failed to load data to null entity.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + if (ModuleID.empty()) + { + LOG.Add("FENativeScriptSystem::AddFailedToLoadData failed to add failed to load data to entity with empty ModuleID.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + FENativeScriptFailedToLoadData* FailedToLoadData = new FENativeScriptFailedToLoadData(); + FailedToLoadData->ModuleID = ModuleID; + FailedToLoadData->RawData = RawData; + + Entity->AddComponent(); + Entity->GetComponent().FailedToLoadData = FailedToLoadData; +} + +FENativeScriptModule* FENativeScriptSystem::GetActiveModule(std::string ModuleID) +{ + if (ActiveModules.find(ModuleID) == ActiveModules.end()) + { + LOG.Add("FENativeScriptSystem::GetActiveModule failed to find active module with ID: " + ModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return nullptr; + } + + return ActiveModules[ModuleID]; +} + +std::vector FENativeScriptSystem::GetActiveModuleScriptNameList(std::string ModuleID) +{ + FENativeScriptModule* Module = GetActiveModule(ModuleID); + if (Module == nullptr) + { + LOG.Add("FENativeScriptSystem::GetActiveModuleScriptNameList failed to find active module with ID: " + ModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::vector(); + } + + std::vector Result; + + auto Iterator = Module->Registry.begin(); + while (Iterator != Module->Registry.end()) + { + std::string ScriptName = Iterator->first; + Result.push_back(ScriptName); + + Iterator++; + } + + return Result; +} + +std::vector FENativeScriptSystem::GetActiveModuleIDList() +{ + std::vector Result; + + auto Iterator = ActiveModules.begin(); + while (Iterator != ActiveModules.end()) + { + std::string ModuleID = Iterator->first; + Result.push_back(ModuleID); + + Iterator++; + } + + return Result; +} + +bool FENativeScriptSystem::InitializeScriptComponent(FEEntity* Entity, std::string ActiveModuleID, std::string ScriptName) +{ + if (Entity == nullptr) + { + LOG.Add("FENativeScriptSystem::InitializeScriptComponent failed to add script component to null entity.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (ActiveModuleID.empty()) + { + LOG.Add("FENativeScriptSystem::InitializeScriptComponent failed to add script component to entity with empty ActiveModuleID.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (ScriptName.empty()) + { + LOG.Add("FENativeScriptSystem::InitializeScriptComponent failed to add script component to entity with empty ScriptName.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + FENativeScriptModule* ActiveModule = GetActiveModule(ActiveModuleID); + if (ActiveModule == nullptr) + { + LOG.Add("FENativeScriptSystem::InitializeScriptComponent failed to find active module with ID: " + ActiveModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (ActiveModule->Registry.find(ScriptName) == ActiveModule->Registry.end()) + { + LOG.Add("FENativeScriptSystem::InitializeScriptComponent failed to find script with name: " + ScriptName + " in module with ID: " + ActiveModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (!Entity->HasComponent()) + { + LOG.Add("FENativeScriptSystem::InitializeScriptComponent failed because entity does not have FENativeScriptComponent.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (!InitializeComponentInternal(Entity, Entity->GetComponent(), ActiveModuleID, ActiveModule->Registry[ScriptName])) + { + LOG.Add("FENativeScriptSystem::InitializeScriptComponent failed to initialize script component.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + return true; +} + +std::unordered_map FENativeScriptSystem::GetVariablesRegistry(FEEntity* Entity) +{ + if (Entity == nullptr) + { + LOG.Add("FENativeScriptSystem::GetVariablesRegistry failed to get field registry from null entity.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::unordered_map(); + } + + if (!Entity->HasComponent()) + { + LOG.Add("FENativeScriptSystem::GetVariablesRegistry failed to get field registry from entity without FENativeScriptComponent.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::unordered_map(); + } + + FENativeScriptComponent& NativeScriptComponent = Entity->GetComponent(); + FENativeScriptModule* Module = RESOURCE_MANAGER.GetNativeScriptModule(NativeScriptComponent.ModuleID); + if (Module == nullptr) + { + LOG.Add("FENativeScriptSystem::GetVariablesRegistry failed to find module with ID: " + NativeScriptComponent.ModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::unordered_map(); + } + + if (!Module->IsLoadedToMemory() || GetActiveModule(NativeScriptComponent.ModuleID) == nullptr) + { + LOG.Add("FENativeScriptSystem::GetVariablesRegistry failed to find active module with ID: " + NativeScriptComponent.ModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::unordered_map(); + } + + return Module->Registry[NativeScriptComponent.GetScriptData()->Name].VariablesRegistry; +} + +std::unordered_map FENativeScriptSystem::GetVariablesRegistry(std::string ModuleID, std::string ScriptName) +{ + FENativeScriptModule* Module = RESOURCE_MANAGER.GetNativeScriptModule(ModuleID); + if (Module == nullptr) + { + LOG.Add("FENativeScriptSystem::GetVariablesRegistry failed to find module with ID: " + ModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::unordered_map(); + } + + if (!Module->IsLoadedToMemory() || GetActiveModule(ModuleID) == nullptr) + { + LOG.Add("FENativeScriptSystem::GetVariablesRegistry failed to find active module with ID: " + ModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::unordered_map(); + } + + if (Module->Registry.find(ScriptName) == Module->Registry.end()) + { + LOG.Add("FENativeScriptSystem::GetVariablesRegistry failed to find script with name: " + ScriptName + " in module with ID: " + ModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::unordered_map(); + } + + return Module->Registry[ScriptName].VariablesRegistry; +} + +bool FENativeScriptSystem::ActivateNativeScriptModule(std::string ModuleID) +{ + FENativeScriptModule* Module = RESOURCE_MANAGER.GetNativeScriptModule(ModuleID); + if (Module == nullptr) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed to find module with ID: " + ModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (ActiveModules.find(ModuleID) != ActiveModules.end()) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed because module with ID: " + ModuleID + " is already activated.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + +#ifndef FOCAL_ENGINE_SHARED + if (!STATIC_CORE_SCRIPT_MANAGER.HaveModuleWithID(Module->GetObjectID())) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule(STATIC) failed because module with ID: " + ModuleID + " is not registered.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } +#endif // FOCAL_ENGINE_SHARED + + return ActivateNativeScriptModule(Module); +} + +#include "../../ResourceManager/Timestamp.h" +bool FENativeScriptSystem::ActivateNativeScriptModule(FENativeScriptModule* Module) +{ + if (Module == nullptr) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed to activate null module.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (ActiveModules.find(Module->GetObjectID()) != ActiveModules.end()) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed because module with ID: " + Module->GetObjectID() + " is already activated.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (Module->IsLoadedToMemory()) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed because module with ID: " + Module->GetObjectID() + " is already loaded to memory.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + +#ifdef FOCAL_ENGINE_SHARED + // First we need to extract the DLL and PDB files to a temporary directory. + std::string ExtractedFolderPath = FILE_SYSTEM.GetCurrentWorkingPath() + "/ExtractedNativeScripts/"; + if (!FILE_SYSTEM.DoesDirectoryExist(ExtractedFolderPath)) + FILE_SYSTEM.CreateDirectory(ExtractedFolderPath); + + ExtractedFolderPath += Module->GetObjectID() + "/"; + if (!FILE_SYSTEM.DoesDirectoryExist(ExtractedFolderPath)) + FILE_SYSTEM.CreateDirectory(ExtractedFolderPath); + + FEAssetPackageAssetInfo AssetInfo; + +#ifdef _DEBUG + AssetInfo = Module->ScriptAssetPackage->GetAssetInfo(Module->DebugDLLAssetID); + std::string DLLPath = ExtractedFolderPath + AssetInfo.Name; + if (!Module->ScriptAssetPackage->ExportAssetToFile(Module->DebugDLLAssetID, DLLPath)) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed to extract DLL file from asset package.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + std::string PDBPath = ""; + if (!Module->DebugPDBAssetID.empty()) + { + AssetInfo = Module->ScriptAssetPackage->GetAssetInfo(Module->DebugPDBAssetID); + if (!Module->ScriptAssetPackage->ExportAssetToFile(Module->DebugPDBAssetID, ExtractedFolderPath + AssetInfo.Name)) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed to extract PDB file from asset package.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + } + + Module->ExtractedPDBPath = PDBPath; +#else + AssetInfo = Module->ScriptAssetPackage->GetAssetInfo(Module->ReleaseDLLAssetID); + std::string DLLPath = ExtractedFolderPath + AssetInfo.Name; + if (!Module->ScriptAssetPackage->ExportAssetToFile(Module->ReleaseDLLAssetID, DLLPath)) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed to extract DLL file from asset package.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } +#endif + + HMODULE DLLHandle = LoadLibraryA(DLLPath.c_str()); + if (!DLLHandle) + { + DWORD ErrorMessageID = ::GetLastError(); + if (ErrorMessageID == 0) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed to load DLL: " + DLLPath + " without error message.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + LPSTR MessageBuffer = nullptr; + size_t Size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, ErrorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&MessageBuffer, 0, NULL); + + std::string StringMessage(MessageBuffer, Size); + LocalFree(MessageBuffer); + + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed to load DLL: " + DLLPath + " with error: " + StringMessage, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + // Ensuring that the DLL has the required functions. + typedef char* (*Get_ModuleID_Function)(void); + Get_ModuleID_Function GetModuleID = (Get_ModuleID_Function)GetProcAddress(DLLHandle, "GetModuleID"); + if (!GetModuleID) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed to get GetModuleID function from DLL: " + DLLPath, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + FreeLibrary(DLLHandle); + return false; + } + + typedef bool (*IsCompiledInDebugMode_Function)(void); + IsCompiledInDebugMode_Function IsCompiledInDebugMode = (IsCompiledInDebugMode_Function)GetProcAddress(DLLHandle, "IsCompiledInDebugMode"); + if (IsCompiledInDebugMode) + { + bool DLLInDebug = IsCompiledInDebugMode(); + +#ifdef _DEBUG + if (!DLLInDebug) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed because DLL with path: " + DLLPath + " was compiled in release mode, while engine is in debug mode.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + FreeLibrary(DLLHandle); + return false; + } +#else + if (DLLInDebug) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed because DLL with path: " + DLLPath + " was compiled in debug mode, while engine is in release mode.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + FreeLibrary(DLLHandle); + return false; + } +#endif + } + + typedef unsigned long long (*Get_EngineHeaders_BuildVersion_Function)(void); + Get_EngineHeaders_BuildVersion_Function GetEngineHeadersBuildVersion = (Get_EngineHeaders_BuildVersion_Function)GetProcAddress(DLLHandle, "GetEngineHeadersBuildVersion"); + if (GetEngineHeadersBuildVersion) + { + unsigned long long EngineHeadersBuildVersion = GetEngineHeadersBuildVersion(); + unsigned long long EngineBuildVersion = std::stoull(ENGINE_BUILD_TIMESTAMP); + if (EngineHeadersBuildVersion != EngineBuildVersion) + { + // Currently we will just log this error, but in the future we should handle this more gracefully. + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule is loading DLL with path: " + DLLPath + " that was compiled with different engine headers version, that could lead to crashes.", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + } + } + + std::string DLLModuleID = GetModuleID(); + if (DLLModuleID.empty() || DLLModuleID.size() != 24) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed to get proper DLLModuleID from DLL: " + DLLPath, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + FreeLibrary(DLLHandle); + return false; + } + + typedef void* (*Get_Script_Registry_Function)(void); + Get_Script_Registry_Function GetScriptRegistry = (Get_Script_Registry_Function)GetProcAddress(DLLHandle, "GetScriptRegistry"); + if (!GetScriptRegistry) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed to get GetScriptRegistry function from DLL: " + DLLPath, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + FreeLibrary(DLLHandle); + return false; + } + + void* RawDLLRegistry = GetScriptRegistry(); + if (!RawDLLRegistry) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule failed to get ScriptRegistry from DLL: " + DLLPath, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + FreeLibrary(DLLHandle); + return false; + } + + std::unordered_map* DLLRegistry = (std::unordered_map*)RawDLLRegistry; + + Module->bIsLoadedToMemory = true; + Module->DLLHandle = DLLHandle; + Module->ExtractedDLLPath = DLLPath; + + // Manually copy the registry to the loaded modules. + auto Iterator = DLLRegistry->begin(); + while (Iterator != DLLRegistry->end()) + { + Module->Registry[Iterator->first].bRunInEditor = Iterator->second.bRunInEditor; + Module->Registry[Iterator->first].Name = Iterator->second.Name; + Module->Registry[Iterator->first].ConstructorFunction = Iterator->second.ConstructorFunction; + Module->Registry[Iterator->first].VariablesRegistry = Iterator->second.VariablesRegistry; + Iterator++; + } +#else + if (!STATIC_CORE_SCRIPT_MANAGER.HaveModuleWithID(Module->GetObjectID())) + { + LOG.Add("FENativeScriptSystem::ActivateNativeScriptModule(STATIC) failed because module with ID: " + Module->GetObjectID() + " is not registered.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + Module->bIsLoadedToMemory = true; + Module->DLLHandle = nullptr; + Module->ExtractedDLLPath = ""; + Module->Registry = STATIC_CORE_SCRIPT_MANAGER.GetRegistryForModuleWithID(Module->GetObjectID()); +#endif // FOCAL_ENGINE_SHARED + + ActiveModules[Module->GetObjectID()] = Module; + return true; +} + +bool FENativeScriptSystem::DeactivateNativeScriptModule(std::string ModuleID) +{ + FENativeScriptModule* Module = RESOURCE_MANAGER.GetNativeScriptModule(ModuleID); + if (Module == nullptr) + { + LOG.Add("FENativeScriptSystem::DeactivateNativeScriptModule failed to find module with ID: " + ModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (ActiveModules.find(ModuleID) == ActiveModules.end()) + { + LOG.Add("FENativeScriptSystem::DeactivateNativeScriptModule failed because module with ID: " + ModuleID + " is not activated.", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return true; + } + + return DeactivateNativeScriptModule(Module); +} + +bool FENativeScriptSystem::DeactivateNativeScriptModule(FENativeScriptModule* Module) +{ + if (Module == nullptr) + { + LOG.Add("FENativeScriptSystem::DeactivateNativeScriptModule failed to deactivate null module.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (ActiveModules.find(Module->GetObjectID()) == ActiveModules.end()) + { + LOG.Add("FENativeScriptSystem::DeactivateNativeScriptModule failed because module with ID: " + Module->GetObjectID() + " is not activated.", "FE_SCRIPT_SYSTEM", FE_LOG_WARNING); + return true; + } + + if (!Module->IsLoadedToMemory()) + { + LOG.Add("FENativeScriptSystem::DeactivateNativeScriptModule failed because module with ID: " + Module->GetObjectID() + " is not loaded to memory.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + // We should delete all script components associated with this module. + ComponentsClearOnModuleDeactivate(Module); + Module->Registry.clear(); + + FreeLibrary(Module->DLLHandle); + Module->bIsLoadedToMemory = false; + Module->DLLHandle = nullptr; + if (!Module->ExtractedDLLPath.empty()) + { + FILE_SYSTEM.DeleteFile(Module->ExtractedDLLPath); + Module->ExtractedDLLPath = ""; + } + + if (!Module->ExtractedPDBPath.empty()) + { + FILE_SYSTEM.DeleteFile(Module->ExtractedPDBPath); + Module->ExtractedPDBPath = ""; + } + + std::string ExtractedFolderPath = FILE_SYSTEM.GetCurrentWorkingPath() + "/ExtractedNativeScripts/"; + ExtractedFolderPath += Module->GetObjectID() + "/"; + if (FILE_SYSTEM.DoesDirectoryExist(ExtractedFolderPath)) + FILE_SYSTEM.DeleteDirectory(ExtractedFolderPath); + + ActiveModules.erase(Module->GetObjectID()); + return true; +} + +void FENativeScriptSystem::RemoveComponentsFromScene(FEScene* Scene, std::string ModuleID) +{ + std::vector Entities = Scene->GetEntityListWithComponent(); + + for (size_t i = 0; i < Entities.size(); i++) + { + FENativeScriptComponent& NativeScriptComponent = Entities[i]->GetComponent(); + if (NativeScriptComponent.ModuleID == ModuleID) + { + NativeScriptComponent.OnDestroy(); + Entities[i]->RemoveComponent(); + } + } +} + +void FENativeScriptSystem::ComponentsClearOnModuleDeactivate(FENativeScriptModule* Module) +{ + if (Module == nullptr) + { + LOG.Add("FENativeScriptSystem::ComponentsClearOnModuleDeactivate failed to clear components on null module.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + std::vector ActiveScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active); + for (FEScene* Scene : ActiveScenes) + RemoveComponentsFromScene(Scene, Module->GetObjectID()); + + // Besides active scenes, we should also check prefab internal scenes. + + // TO DO: That is an interesting way of handling prefabs scenes, but I am not sure if it is the best way. + //std::vector PrefabInternalScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::PrefabInternal); + + std::vector PrefabIDList = RESOURCE_MANAGER.GetPrefabIDList(); + for (size_t i = 0; i < PrefabIDList.size(); i++) + { + FEPrefab* CurrentPrefab = RESOURCE_MANAGER.GetPrefab(PrefabIDList[i]); + FEScene* PrefabInternalScene = CurrentPrefab->GetScene(); + + if (PrefabInternalScene == nullptr) + continue; + + RemoveComponentsFromScene(PrefabInternalScene, Module->GetObjectID()); + } +} + +void FENativeScriptSystem::GetModuleScriptInstancesFromScene(std::vector& Result, FEScene* Scene, std::string ModuleID) +{ + if (Scene == nullptr) + { + LOG.Add("FENativeScriptSystem::GetModuleScriptInstancesFromScene failed to get components of null scene.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + std::vector Entities = Scene->GetEntityListWithComponent(); + + for (size_t i = 0; i < Entities.size(); i++) + { + FENativeScriptComponent& NativeScriptComponent = Entities[i]->GetComponent(); + if (NativeScriptComponent.ModuleID == ModuleID) + { + FEModuleScriptInstance Instance; + Instance.Scene = Scene; + Instance.Entity = Entities[i]; + //Instance.NativeScriptComponent = &NativeScriptComponent; + Instance.ScriptName = NativeScriptComponent.GetScriptData()->Name; + + Result.push_back(Instance); + } + } +} + +// Returns array of information about components associated with module. +std::vector FENativeScriptSystem::GetModuleScriptInstances(FENativeScriptModule* Module) +{ + std::vector Result; + + if (Module == nullptr) + { + LOG.Add("FENativeScriptSystem::GetModuleScriptInstances failed to get components of null module.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return Result; + } + + std::vector ActiveScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active); + for (FEScene* Scene : ActiveScenes) + GetModuleScriptInstancesFromScene(Result, Scene, Module->GetObjectID()); + + // Besides active scenes, we should also check prefab internal scenes. + std::vector PrefabIDList = RESOURCE_MANAGER.GetPrefabIDList(); + for (size_t i = 0; i < PrefabIDList.size(); i++) + { + FEPrefab* CurrentPrefab = RESOURCE_MANAGER.GetPrefab(PrefabIDList[i]); + FEScene* PrefabInternalScene = CurrentPrefab->GetScene(); + + if (PrefabInternalScene == nullptr) + continue; + + GetModuleScriptInstancesFromScene(Result, PrefabInternalScene, Module->GetObjectID()); + } + + return Result; +} + +void FENativeScriptSystem::DeleteNativeScriptModule(std::string ModuleID) +{ + FENativeScriptModule* Module = RESOURCE_MANAGER.GetNativeScriptModule(ModuleID); + if (Module == nullptr) + { + LOG.Add("FENativeScriptSystem::DeleteNativeScriptModule failed to find module with ID: " + ModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + DeleteNativeScriptModule(Module); +} + +void FENativeScriptSystem::DeleteNativeScriptModule(FENativeScriptModule* Module) +{ + if (Module == nullptr) + { + LOG.Add("FENativeScriptSystem::DeleteNativeScriptModule failed to delete null module.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return; + } + + if (ActiveModules.find(Module->GetObjectID()) != ActiveModules.end()) + DeactivateNativeScriptModule(Module); + + RESOURCE_MANAGER.DeleteNativeScriptModuleInternal(Module); +} + +bool FENativeScriptSystem::UpdateNativeScriptModule(std::string CurrentModuleID, std::string UpdatedModuleID) +{ + FENativeScriptModule* CurrentModule = RESOURCE_MANAGER.GetNativeScriptModule(CurrentModuleID); + if (CurrentModule == nullptr) + { + LOG.Add("FENativeScriptSystem::UpdateNativeScriptModule failed to find current module with ID: " + CurrentModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + FENativeScriptModule* UpdatedModule = RESOURCE_MANAGER.GetNativeScriptModule(UpdatedModuleID); + if (UpdatedModule == nullptr) + { + LOG.Add("FENativeScriptSystem::UpdateNativeScriptModule failed to find updated module with ID: " + UpdatedModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (ActiveModules.find(CurrentModuleID) == ActiveModules.end()) + { + LOG.Add("FENativeScriptSystem::UpdateNativeScriptModule failed to find active module with ID: " + CurrentModuleID, "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + return UpdateNativeScriptModule(CurrentModule, UpdatedModule); +} + +std::any FENativeScriptSystem::CreateEngineLocalScriptVariableCopy(FEScriptVariableInfo& Info, std::any Value) +{ + // TO-DO: Make it more general with more templated magic. + if (Info.Type == "float") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "int") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "bool") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "std::string") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "glm::vec2") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "glm::vec3") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "glm::vec4") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "glm::quat") return CreateEngineLocalScriptVariableCopyTemplated(Value); + // ************************ FEObject children ************************ + else if (Info.Type == "FEShader*") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "FEMesh*") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "FETexture*") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "FEMaterial*") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "FEGameModel*") return CreateEngineLocalScriptVariableCopyTemplated(Value); + // TO-DO: Think how to make it work with enitity. Right now it is not consistent between editor and game mode scenes. + //else if (Info.Type == "FEEntity*") return CreateLocalCopy(Value); + else if (Info.Type == "FEFramebuffer*") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "FEPostProcess*") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "FEPrefab*") return CreateEngineLocalScriptVariableCopyTemplated(Value); + // TO-DO: Check if it is working. + else if (Info.Type == "FEScene*") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "FEAssetPackage*") return CreateEngineLocalScriptVariableCopyTemplated(Value); + else if (Info.Type == "FENativeScriptModule*") return CreateEngineLocalScriptVariableCopyTemplated(Value); + // ************************ Vectors ************************ + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + // ************************ FEObject children ************************ + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + // TO-DO: Think how to make it work with enitity. Right now it is not consistent between editor and game mode scenes. + //else if (Info.Type == "std::vector") return CreateLocalCopy>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + // TO-DO: Check if it is working. + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + else if (Info.Type == "std::vector") return CreateEngineLocalScriptVariableCopyTemplated>(Value); + + // If we can't find the type, we will return empty any. + return std::any(); +} + +void FENativeScriptSystem::CheckForAlteredVariables(std::vector& ModuleScriptInstancesToUpdate) +{ + for (size_t i = 0; i < ModuleScriptInstancesToUpdate.size(); i++) + { + FENativeScriptComponent& NativeScriptComponent = ModuleScriptInstancesToUpdate[i].Entity->GetComponent(); + std::unordered_map Variables = NATIVE_SCRIPT_SYSTEM.GetVariablesRegistry(ModuleScriptInstancesToUpdate[i].Entity); + + FEScriptData* CurrentScriptData = NativeScriptComponent.ScriptData; + // We need to create an instance of the original core. + // To have access to the original variable values. + FENativeScriptCore* OriginalCoreInstance = CurrentScriptData->ConstructorFunction(); + + auto VariableIterator = Variables.begin(); + while (VariableIterator != Variables.end()) + { + FEScriptVariableInfo& VariableInfo = VariableIterator->second; + + std::any OriginalValue = VariableInfo.Getter(OriginalCoreInstance); + std::any CurrentValue = NativeScriptComponent.GetVariableValueRaw(VariableInfo.Name); + + if (CurrentScriptData->VariablesRegistry.find(VariableInfo.Name) != CurrentScriptData->VariablesRegistry.end()) + { + if (!IsEqualScriptVariable(VariableInfo, OriginalValue, CurrentValue)) + { + FEAlteredScriptVariable AlteredVariable; + AlteredVariable.Name = VariableInfo.Name; + + // We need to create a local(Engine local, not DLL resident) copy of the altered variable. + // Because after we unload the DLL, we will lose access to the original variable. + AlteredVariable.AlteredValue = CreateEngineLocalScriptVariableCopy(VariableInfo, CurrentValue); + + if (!AlteredVariable.AlteredValue.has_value()) + { + LOG.Add("FENativeScriptSystem::UpdateNativeScriptModule failed to create local copy of altered variable.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + VariableIterator++; + continue; + } + + ModuleScriptInstancesToUpdate[i].AlteredVariables.push_back(AlteredVariable); + } + + } + + VariableIterator++; + } + + // Now we can delete the original core instance. + delete OriginalCoreInstance; + } +} + +bool FENativeScriptSystem::UpdateNativeScriptModule(FENativeScriptModule* CurrentModule, FENativeScriptModule* UpdatedModule) +{ + if (CurrentModule == nullptr) + { + LOG.Add("FENativeScriptSystem::UpdateNativeScriptModule failed to update null current module.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + if (UpdatedModule == nullptr) + { + LOG.Add("FENativeScriptSystem::UpdateNativeScriptModule failed to update null updated module.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + // First we need to get the list of components associated with the current module. + std::vector ComponentToRestore = GetModuleScriptInstances(CurrentModule); + // Now we need to loop through the components and save script variables that was altered by user. + CheckForAlteredVariables(ComponentToRestore); + + // Then we can deactivate and delete current module. + DeactivateNativeScriptModule(CurrentModule); + DeleteNativeScriptModule(CurrentModule); + + // And activate updated module. + ActivateNativeScriptModule(UpdatedModule); + + // Now we can restore the components and altered variables. + bool bAtLeastOneComponentRestored = false; + for (size_t i = 0; i < ComponentToRestore.size(); i++) + { + ComponentToRestore[i].Entity->AddComponent(); + if (InitializeScriptComponent(ComponentToRestore[i].Entity, UpdatedModule->GetObjectID(), ComponentToRestore[i].ScriptName)) + { + bAtLeastOneComponentRestored = true; + + for (size_t j = 0; j < ComponentToRestore[i].AlteredVariables.size(); j++) + { + FENativeScriptComponent& NativeScriptComponent = ComponentToRestore[i].Entity->GetComponent(); + NativeScriptComponent.SetVariableValue(ComponentToRestore[i].AlteredVariables[j].Name, ComponentToRestore[i].AlteredVariables[j].AlteredValue); + } + } + else + { + LOG.Add("FENativeScriptSystem::UpdateNativeScriptModule failed to restore component with script name: " + ComponentToRestore[i].ScriptName + " to entity.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + ComponentToRestore[i].Entity->RemoveComponent(); + } + } + + return bAtLeastOneComponentRestored; +} + +bool FENativeScriptSystem::ReloadDLL(FENativeScriptModule* ModuleToUpdate) +{ + if (ModuleToUpdate == nullptr) + { + LOG.Add("FENativeScriptSystem::ReloadDLL failed to update null module.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + // First we need to get the list of components associated with the current module. + std::vector ComponentToRestore = GetModuleScriptInstances(ModuleToUpdate); + // Now we need to loop through the components and save script variables that was altered by user. + CheckForAlteredVariables(ComponentToRestore); + + // Then we can deactivate module. + if (!DeactivateNativeScriptModule(ModuleToUpdate)) + { + LOG.Add("FENativeScriptSystem::ReloadDLL failed to deactivate module with ID: " + ModuleToUpdate->GetObjectID(), "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + // And activate it again. + if (!ActivateNativeScriptModule(ModuleToUpdate)) + { + LOG.Add("FENativeScriptSystem::ReloadDLL failed to activate module with ID: " + ModuleToUpdate->GetObjectID(), "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } + + // Now we can restore the components and altered variables. + bool bAtLeastOneComponentRestored = false; + for (size_t i = 0; i < ComponentToRestore.size(); i++) + { + ComponentToRestore[i].Entity->AddComponent(); + if (InitializeScriptComponent(ComponentToRestore[i].Entity, ModuleToUpdate->GetObjectID(), ComponentToRestore[i].ScriptName)) + { + bAtLeastOneComponentRestored = true; + + for (size_t j = 0; j < ComponentToRestore[i].AlteredVariables.size(); j++) + { + FENativeScriptComponent& NativeScriptComponent = ComponentToRestore[i].Entity->GetComponent(); + NativeScriptComponent.SetVariableValue(ComponentToRestore[i].AlteredVariables[j].Name, ComponentToRestore[i].AlteredVariables[j].AlteredValue); + } + } + else + { + LOG.Add("FENativeScriptSystem::ReloadDLL failed to restore component with script name: " + ComponentToRestore[i].ScriptName + " to entity.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + ComponentToRestore[i].Entity->RemoveComponent(); + } + } + + if (ComponentToRestore.empty()) + bAtLeastOneComponentRestored = true; + + return bAtLeastOneComponentRestored; +} + +bool FENativeScriptSystem::IsEqualScriptVariable(FEScriptVariableInfo& VariableInfo, std::any FirstScriptVariable, std::any SecondScriptVariable) +{ + if (VariableInfo.Type.find("vector<") != std::string::npos || VariableInfo.Type.find("std::vector<") != std::string::npos) + { + //return true; + if (VariableInfo.Type.find("float>") != std::string::npos) return IsEqualArrayTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type.find("int>") != std::string::npos) return IsEqualArrayTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type.find("bool>") != std::string::npos) return IsEqualArrayTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type.find("std::string>") != std::string::npos) return IsEqualArrayTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type.find("glm::vec2>") != std::string::npos) return IsEqualArrayTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type.find("glm::vec3>") != std::string::npos) return IsEqualArrayTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type.find("glm::vec4>") != std::string::npos) return IsEqualArrayTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type.find("glm::quat>") != std::string::npos) return IsEqualArrayTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type.find("FEPrefab*>") != std::string::npos) return IsEqualArrayTType(FirstScriptVariable, SecondScriptVariable); + } + else + { + if (VariableInfo.Type == "int") return IsEqualTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type == "float") return IsEqualTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type == "double") return IsEqualTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type == "bool") return IsEqualTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type == "glm::vec2") return IsEqualTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type == "glm::vec3") return IsEqualTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type == "glm::vec4") return IsEqualTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type == "glm::quat") return IsEqualTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type == "string") return IsEqualTType(FirstScriptVariable, SecondScriptVariable); + else if (VariableInfo.Type == "FEPrefab*") return IsEqualTType(FirstScriptVariable, SecondScriptVariable); + } + + return false; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.h b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.h new file mode 100644 index 0000000..7f812f5 --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.h @@ -0,0 +1,130 @@ +#pragma once +#include "../../Scene/FESceneManager.h" + +namespace FocalEngine +{ + struct FEAlteredScriptVariable + { + std::string Name; + std::any AlteredValue; + }; + + struct FEModuleScriptInstance + { + FEScene* Scene; + FEEntity* Entity; + std::string ScriptName; + std::vector AlteredVariables; + }; + + class FOCAL_ENGINE_API FENativeScriptSystem + { + friend class FEScene; + friend class FERenderer; + friend class FEngine; + + SINGLETON_PRIVATE_PART(FENativeScriptSystem) + + static void OnMyComponentAdded(FEEntity* Entity); + static void OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing); + void RegisterOnComponentCallbacks(); + + void Update(double DeltaTime); + + static Json::Value NativeScriptComponentToJson(FEEntity* Entity); + static void NativeScriptComponentFromJson(FEEntity* Entity, Json::Value Root); + static void DuplicateNativeScriptComponent(FEEntity* SourceEntity, FEEntity* TargetEntity); + void AddFailedToLoadData(FEEntity* Entity, std::string ModuleID, Json::Value RawData); + + bool InitializeComponentInternal(FEEntity* Entity, FENativeScriptComponent& NativeScriptComponent, std::string ActiveModuleID, FEScriptData& ScriptData); + FENativeScriptModule* GetActiveModule(std::string ModuleID); + + void CopyVariableValuesInternal(FENativeScriptComponent* SourceComponent, FENativeScriptComponent* TargetComponent); + + // Returns array of information about components associated with module. + std::vector GetModuleScriptInstances(FENativeScriptModule* Module); + void GetModuleScriptInstancesFromScene(std::vector& Result, FEScene* Scene, std::string ModuleID); + + // Function will check component for user altered variables, if any found, it will update list of components accordingly. + void CheckForAlteredVariables(std::vector& ModuleScriptInstancesToUpdate); + std::any CreateEngineLocalScriptVariableCopy(FEScriptVariableInfo& Info, std::any Value); + template + std::any CreateEngineLocalScriptVariableCopyTemplated(std::any Value); + + // We should delete all script components associated with module. + void ComponentsClearOnModuleDeactivate(FENativeScriptModule* Module); + + void RemoveComponentsFromScene(FEScene* Scene, std::string ModuleID); + + template + T* CastScript(FENativeScriptCore* Core); + + template + std::any LoadVariableTTypeToJSON(const Json::Value& Root); + template + std::any LoadArrayVariableTTypeToJSON(const Json::Value& Root); + void LoadVariableFromJSON(Json::Value& Root, FEScriptVariableInfo& VariableInfo, FENativeScriptCore* Core); + + template + void SaveVariableTTypeToJSON(Json::Value& Root, std::any AnyValue); + template + void SaveArrayVariableTTypeToJSON(Json::Value& Root, std::any AnyValue); + void SaveVariableToJSON(Json::Value& Root, FEScriptVariableInfo& VariableInfo, FENativeScriptCore* Core); + + template + bool IsEqualTType(std::any FirstScriptVariable, std::any SecondScriptVariable); + + template + bool IsEqualArrayTType(std::any FirstScriptVariable, std::any SecondScriptVariable); + + bool IsEqualScriptVariable(FEScriptVariableInfo& VariableInfo, std::any FirstScriptVariable, std::any SecondScriptVariable); + public: + SINGLETON_PUBLIC_PART(FENativeScriptSystem) + + std::unordered_map ActiveModules; + + bool ActivateNativeScriptModule(std::string ModuleID); + bool ActivateNativeScriptModule(FENativeScriptModule* Module); + + bool DeactivateNativeScriptModule(std::string ModuleID); + bool DeactivateNativeScriptModule(FENativeScriptModule* Module); + + void DeleteNativeScriptModule(std::string ModuleID); + void DeleteNativeScriptModule(FENativeScriptModule* Module); + + // Try to update module with new one. It will succeed only if new module has some script names that old module has. + // Should be used only for hot-reloading of new version of same module. + bool UpdateNativeScriptModule(std::string CurrentModuleID, std::string UpdatedModuleID); + // Try to update module with new one. It will succeed only if new module has some script names that old module has. + // Should be used only for hot-reloading of new version of same module. + bool UpdateNativeScriptModule(FENativeScriptModule* CurrentModule, FENativeScriptModule* UpdatedModule); + + bool ReloadDLL(FENativeScriptModule* ModuleToUpdate); + + std::vector GetActiveModuleIDList(); + std::vector GetActiveModuleScriptNameList(std::string ModuleID); + + bool InitializeScriptComponent(FEEntity* Entity, std::string ActiveModuleID, std::string ScriptName); + + std::unordered_map GetVariablesRegistry(FEEntity* Entity); + std::unordered_map GetVariablesRegistry(std::string ModuleID, std::string ScriptName); + + template + T* CastToScriptClass(FENativeScriptComponent& Component); + + template + std::vector GetEntityListWithScript(FEScene* Scene); + + template + std::vector GetScriptList(FEScene* Scene); + }; + +#include "FENativeScriptSystem.inl" + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetNativeScriptSystem(); + #define NATIVE_SCRIPT_SYSTEM (*static_cast(GetNativeScriptSystem())) +#else + #define NATIVE_SCRIPT_SYSTEM FENativeScriptSystem::GetInstance() +#endif +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.inl b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.inl new file mode 100644 index 0000000..9d97d25 --- /dev/null +++ b/SubSystems/Scene/Components/NativeScriptSystem/FENativeScriptSystem.inl @@ -0,0 +1,283 @@ +#pragma once + +template +T* FENativeScriptSystem::CastScript(FENativeScriptCore* Core) +{ + if (Core == nullptr) + { + LOG.Add("FENativeScriptSystem::CastScript: Core is nullptr", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return nullptr; + } + + static_assert(std::is_base_of::value, + "T must inherit from FENativeScriptCore"); + + T* CastedScript = dynamic_cast(Core); + if (CastedScript == nullptr) + LOG.Add("FENativeScriptSystem::CastScript: Failed to cast to " + std::string(typeid(T).name()), "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + + return CastedScript; +} + +template +T* FENativeScriptSystem::CastToScriptClass(FENativeScriptComponent& Component) +{ + return CastScript(Component.GetCoreInstance()); +} + +template +void FENativeScriptSystem::SaveVariableTTypeToJSON(Json::Value& Root, std::any AnyValue) +{ + T Value = std::any_cast(AnyValue); + + if constexpr (std::is_same_v) Root = Value; + else if constexpr (std::is_same_v) Root = Value; + else if constexpr (std::is_same_v) Root = Value; + else if constexpr (std::is_same_v) Root = Value; + else if constexpr (std::is_same_v) Root = Value; + else if constexpr (std::is_same_v) + { + Root["X"] = Value.x; + Root["Y"] = Value.y; + } + else if constexpr (std::is_same_v) + { + Root["X"] = Value.x; + Root["Y"] = Value.y; + Root["Z"] = Value.z; + } + else if constexpr (std::is_same_v) + { + Root["X"] = Value.x; + Root["Y"] = Value.y; + Root["Z"] = Value.z; + Root["W"] = Value.w; + } + else if constexpr (std::is_same_v) + { + Root["X"] = Value.x; + Root["Y"] = Value.y; + Root["Z"] = Value.z; + Root["W"] = Value.w; + } + else if constexpr (std::is_same_v) + { + Root["ID"] = Value == nullptr ? "" : Value->GetObjectID(); + } +} + +template +void FENativeScriptSystem::SaveArrayVariableTTypeToJSON(Json::Value& Root, std::any AnyValue) +{ + std::vector Value = std::any_cast>(AnyValue); + for (size_t i = 0; i < Value.size(); i++) + { + Json::Value Element; + SaveVariableTTypeToJSON(Element, Value[i]); + Root[std::to_string(i)] = Element; + } +} + +template +std::any FENativeScriptSystem::LoadVariableTTypeToJSON(const Json::Value& Root) +{ + if constexpr (std::is_same_v) + { + T Value = Root.as(); + return Value; + } + else if constexpr (std::is_same_v) + { + T Value = Root.as(); + return Value; + } + else if constexpr (std::is_same_v) + { + T Value = Root.as(); + return Value; + } + else if constexpr (std::is_same_v) + { + T Value = Root.as(); + return Value; + } + else if constexpr (std::is_same_v) + { + T Value = Root.asString(); + return Value; + } + else if constexpr (std::is_same_v) + { + glm::vec2 Value; + Value.x = Root["X"].as(); + Value.y = Root["Y"].as(); + + return Value; + } + else if constexpr (std::is_same_v) + { + glm::vec3 Value; + Value.x = Root["X"].as(); + Value.y = Root["Y"].as(); + Value.z = Root["Z"].as(); + + return Value; + } + else if constexpr (std::is_same_v) + { + glm::vec4 Value; + Value.x = Root["X"].as(); + Value.y = Root["Y"].as(); + Value.z = Root["Z"].as(); + Value.w = Root["W"].as(); + + return Value; + } + else if constexpr (std::is_same_v) + { + glm::quat Value; + Value.x = Root["X"].as(); + Value.y = Root["Y"].as(); + Value.z = Root["Z"].as(); + Value.w = Root["W"].as(); + + return Value; + } + else if constexpr (std::is_same_v) + { + FEPrefab* Value = nullptr; + std::string PrefabID = Root["ID"].asString(); + if (!PrefabID.empty()) + Value = RESOURCE_MANAGER.GetPrefab(PrefabID); + + return Value; + } + + return T(); +} + +template +std::any FENativeScriptSystem::LoadArrayVariableTTypeToJSON(const Json::Value& Root) +{ + std::vector Value; + for (size_t i = 0; i < Root.size(); i++) + { + T Element = std::any_cast(LoadVariableTTypeToJSON(Root[std::to_string(i)])); + Value.push_back(Element); + } + + return Value; +} + +template +bool FENativeScriptSystem::IsEqualTType(std::any FirstScriptVariable, std::any SecondScriptVariable) +{ + if (FirstScriptVariable.type() != typeid(T) || SecondScriptVariable.type() != typeid(T)) + return false; + + try + { + return std::any_cast(FirstScriptVariable) == std::any_cast(SecondScriptVariable); + } + catch (const std::bad_any_cast& e) + { + LOG.Add("FENativeScriptSystem::IsEqual: " + std::string(e.what()), "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return false; + } +} + +template +bool FENativeScriptSystem::IsEqualArrayTType(std::any FirstScriptVariable, std::any SecondScriptVariable) +{ + if (FirstScriptVariable.type() != typeid(std::vector) || SecondScriptVariable.type() != typeid(std::vector)) + return false; + + std::vector FirstArray = std::any_cast>(FirstScriptVariable); + std::vector SecondArray = std::any_cast>(SecondScriptVariable); + + if (FirstArray.size() != SecondArray.size()) + return false; + + for (size_t i = 0; i < FirstArray.size(); i++) + { + if (FirstArray[i] != SecondArray[i]) + return false; + } + + return true; +} + +template +std::any FENativeScriptSystem::CreateEngineLocalScriptVariableCopyTemplated(std::any Value) +{ + try + { + if constexpr (std::is_pointer_v) + { + // Handle pointer types + auto Pointer = std::any_cast(Value); + if (Pointer == nullptr) + { + return std::any(); + } + else + { + // Currently we support only childs of FEObject. + if (std::is_base_of>::value) + { + std::string ObjectID = Pointer->GetObjectID(); + FEObject* LocalPointer = OBJECT_MANAGER.GetFEObject(ObjectID); + return std::any(static_cast(LocalPointer)); + } + else + { + LOG.Add("FENativeScriptSystem::CreateEngineLocalScriptVariableCopyTemplated failed to create local copy of pointer type because it is not a child of FEObject.", "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::any(); + } + } + } + else + { + // Handle non-pointer types + T LocalCopy = std::any_cast(Value); + return std::any_cast(LocalCopy); + } + } + catch (const std::bad_any_cast& Error) + { + LOG.Add("FENativeScriptSystem::CreateEngineLocalScriptVariableCopyTemplated failed to create local copy of variable. Error: " + std::string(Error.what()), "FE_SCRIPT_SYSTEM", FE_LOG_ERROR); + return std::any(); + } +} + +template +std::vector FENativeScriptSystem::GetEntityListWithScript(FEScene* Scene) +{ + std::vector Result; + for (auto& EntityID : Scene->GetEntityIDList()) + { + FEEntity* Entity = Scene->GetEntity(EntityID); + if (Entity->HasComponent()) + { + FENativeScriptComponent& NativeScriptComponent = Entity->GetComponent(); + T* Script = CastScript(NativeScriptComponent.GetCoreInstance()); + if (Script != nullptr) + Result.push_back(Entity); + } + } + return Result; +} + +template +std::vector FENativeScriptSystem::GetScriptList(FEScene* Scene) +{ + std::vector Result; + std::vector EntityList = GetEntityListWithScript(Scene); + for (auto& Entity : EntityList) + { + FENativeScriptComponent& NativeScriptComponent = Entity->GetComponent(); + Result.push_back(CastToScriptClass(NativeScriptComponent)); + } + + return Result; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FECameraSystem.cpp b/SubSystems/Scene/Components/Systems/FECameraSystem.cpp new file mode 100644 index 0000000..657567e --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FECameraSystem.cpp @@ -0,0 +1,433 @@ +#include "FECameraSystem.h" +#include "../FEngine.h" +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetCameraSystem() +{ + return FECameraSystem::GetInstancePointer(); +} +#endif + +FECameraSystem::FECameraSystem() +{ + RegisterOnComponentCallbacks(); + ENGINE.AddOnViewportResizeCallback(OnViewportResize); + + COMPONENTS_TOOL.RegisterComponentToJsonFunction(CameraComponentToJson); + COMPONENTS_TOOL.RegisterComponentFromJsonFunction(CameraComponentFromJson); + COMPONENTS_TOOL.RegisterComponentDuplicateFunction(DuplicateCameraComponent); +} + +void FECameraSystem::RegisterOnComponentCallbacks() +{ + SCENE_MANAGER.RegisterOnComponentConstructCallback(OnMyComponentAdded); + SCENE_MANAGER.RegisterOnComponentDestroyCallback(OnMyComponentDestroy); +} + +void FECameraSystem::OnMyComponentAdded(FEEntity* Entity) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FECameraComponent& CameraComponent = Entity->GetComponent(); +} + +void FECameraSystem::DuplicateCameraComponent(FEEntity* SourceEntity, FEEntity* TargetEntity) +{ + if (SourceEntity == nullptr || TargetEntity == nullptr || !SourceEntity->HasComponent()) + return; + + FECameraComponent& OriginalCameraComponent = SourceEntity->GetComponent(); + TargetEntity->AddComponent(); + + FECameraComponent& NewCameraComponent = TargetEntity->GetComponent(); + NewCameraComponent = OriginalCameraComponent; + // By default, duplicated camera component should not be a main camera. + NewCameraComponent.bIsMainCamera = false; + // But if component is duplicated as part of scene duplication, it could be a main camera. + // If no other camera is set as main camera in parent scene, it will be set as main camera. + if (CAMERA_SYSTEM.GetMainCamera(TargetEntity->GetParentScene()) == nullptr) + NewCameraComponent.bIsMainCamera = true; +} + +void FECameraSystem::OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FECameraComponent& CameraComponent = Entity->GetComponent(); + + auto DataIterator = RENDERER.CameraRenderingDataMap.begin(); + while (DataIterator != RENDERER.CameraRenderingDataMap.end()) + { + if (DataIterator->second->CameraEntity == Entity) + { + delete DataIterator->second; + RENDERER.CameraRenderingDataMap.erase(DataIterator); + break; + } + + DataIterator++; + } + + auto ViewPortIterator = CAMERA_SYSTEM.ViewPortToCameraEntities.begin(); + while (ViewPortIterator != CAMERA_SYSTEM.ViewPortToCameraEntities.end()) + { + for (size_t i = 0; i < ViewPortIterator->second.size(); i++) + { + if (ViewPortIterator->second[i] == Entity) + { + ViewPortIterator->second.erase(ViewPortIterator->second.begin() + i); + break; + } + } + + ViewPortIterator++; + } +} + +FECameraSystem::~FECameraSystem() {}; + +void FECameraSystem::SetMainCamera(FEEntity* CameraEntity) +{ + if (CameraEntity == nullptr || !CameraEntity->HasComponent()) + { + LOG.Add("FECameraSystem::SetMainCamera CameraEntity is nullptr or does not have a camera component.", "FE_LOG_ECS", FE_LOG_ERROR); + return; + } + + if (CameraEntity->GetParentScene() == nullptr) + { + LOG.Add("FECameraSystem::SetMainCamera CameraEntity does not have a parent scene.", "FE_LOG_ECS", FE_LOG_ERROR); + return; + } + + FECameraComponent& CameraComponent = CameraEntity->GetComponent(); + + std::vector EntitiesWithCameraComponent = CameraEntity->GetParentScene()->GetEntityIDListWithComponent(); + // Loop through all camera components and set them to not be the main camera. + for (const std::string& EntityID : EntitiesWithCameraComponent) + { + FEEntity* Entity = CameraEntity->GetParentScene()->GetEntity(EntityID); + if (Entity == nullptr || !Entity->HasComponent()) + continue; + + FECameraComponent& OtherCameraComponent = Entity->GetComponent(); + OtherCameraComponent.bIsMainCamera = false; + } + + CameraComponent.bIsMainCamera = true; +} + +FEEntity* FECameraSystem::GetMainCamera(FEScene* Scene) const +{ + if (Scene == nullptr) + { + LOG.Add("FECameraSystem::GetMainCamera Scene is nullptr.", "FE_LOG_ECS", FE_LOG_ERROR); + return nullptr; + } + + std::vector EntitiesWithCameraComponent = Scene->GetEntityIDListWithComponent(); + for (const std::string& EntityID : EntitiesWithCameraComponent) + { + FEEntity* Entity = Scene->GetEntity(EntityID); + if (Entity == nullptr || !Entity->HasComponent()) + continue; + + FECameraComponent& CameraComponent = Entity->GetComponent(); + if (CameraComponent.bIsMainCamera) + return Entity; + } + + return nullptr; +} + +void FECameraSystem::OnViewportResize(std::string ViewportID) +{ + if (ViewportID.empty()) + { + LOG.Add("FECameraSystem::OnViewportResize ViewportID is empty.", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } + + // Check if we are managing cameras for this viewport. + if (CAMERA_SYSTEM.ViewPortToCameraEntities.find(ViewportID) == CAMERA_SYSTEM.ViewPortToCameraEntities.end()) + return; + + std::vector CameraEntities = CAMERA_SYSTEM.ViewPortToCameraEntities[ViewportID]; + for (FEEntity* CameraEntity : CameraEntities) + { + if (CameraEntity == nullptr || !CameraEntity->HasComponent()) + continue; + + FECameraComponent& CameraComponent = CameraEntity->GetComponent(); + if (CameraComponent.Viewport->Width <= 0 || CameraComponent.Viewport->Height <= 0) + break; + + CameraComponent.AspectRatio = static_cast(CameraComponent.Viewport->GetWidth()) / static_cast(CameraComponent.Viewport->GetHeight()); + RENDERER.ForceCameraRenderingDataUpdate(CameraEntity); + } +} + +bool FECameraSystem::SetCameraRenderScale(FEEntity* CameraEntity, float NewValue) +{ + if (CameraEntity == nullptr || !CameraEntity->HasComponent()) + { + LOG.Add("FECameraSystem::SetCameraRenderScale CameraEntity is nullptr or does not have a camera component.", "FE_LOG_RENDERING", FE_LOG_ERROR); + return false; + } + + if (NewValue <= 0.0f) + { + LOG.Add("FECameraSystem::SetCameraRenderScale Render scale is less than or equal to zero.", "FE_LOG_RENDERING", FE_LOG_WARNING); + return false; + } + + FECameraComponent& CameraComponent = CameraEntity->GetComponent(); + // If render scale is not changed, we do not need to update anything. + if (abs(CameraComponent.RenderScale - NewValue) < 0.0001f) + return false; + + CameraComponent.RenderScale = NewValue; + RENDERER.ForceCameraRenderingDataUpdate(CameraEntity); + + return true; +} + +void FECameraSystem::Update(const double DeltaTime) +{ + std::vector ActiveScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active | FESceneFlag::Renderable); + for (FEScene* Scene : ActiveScenes) + { + std::vector EntitiesWithCameraComponent = Scene->GetEntityIDListWithComponent(); + for (const std::string& EntityID : EntitiesWithCameraComponent) + { + FEEntity* Entity = Scene->GetEntity(EntityID); + if (Entity == nullptr || !Entity->HasComponent()) + continue; + + IndividualUpdate(Entity, DeltaTime); + } + } +} + +void FECameraSystem::IndividualUpdate(FEEntity* CameraEntity, const double DeltaTime) +{ + if (CameraEntity == nullptr || !CameraEntity->HasComponent()) + { + LOG.Add("FECameraSystem::IndividuallyUpdate CameraEntity is nullptr or does not have a camera component.", "FE_LOG_ECS", FE_LOG_ERROR); + return; + } + + FETransformComponent& TransformComponent = CameraEntity->GetComponent(); + FECameraComponent& CameraComponent = CameraEntity->GetComponent(); + + // If no script is attached, we will use default camera view matrix update. + if (!CameraEntity->HasComponent()) + CameraComponent.ViewMatrix = glm::inverse(TransformComponent.GetWorldMatrix()); + + CameraComponent.ProjectionMatrix = glm::perspective(glm::radians(CameraComponent.FOV), CameraComponent.AspectRatio, CameraComponent.NearPlane, CameraComponent.FarPlane); + + if (CameraComponent.IsTemporalJitterEnabled()) + { + CameraComponent.UpdateTemporalJitterOffset(); + + glm::mat4 JitterMatrix = glm::translate(glm::mat4(1.0f), + glm::vec3(2.0f * CameraComponent.GetTemporalJitterOffset().x / CameraComponent.GetRenderTargetWidth(), + -2.0f * CameraComponent.GetTemporalJitterOffset().y / CameraComponent.GetRenderTargetHeight(), 0.0f)); + CameraComponent.ProjectionMatrix = JitterMatrix * CameraComponent.ProjectionMatrix; + } + + CameraComponent.PreviousFrameViewMatrix = CameraComponent.ViewMatrix; +} + +bool FECameraSystem::SetCameraViewport(FEEntity* CameraEntity, std::string ViewportID) +{ + if (CameraEntity == nullptr || !CameraEntity->HasComponent()) + { + LOG.Add("FECameraSystem::SetCameraViewport CameraEntity is nullptr or does not have a camera component.", "FE_LOG_ECS", FE_LOG_ERROR); + return false; + } + + if (ViewportID.empty()) + { + LOG.Add("FECameraSystem::SetCameraViewport ViewportID is empty.", "FE_LOG_ECS", FE_LOG_ERROR); + return false; + } + + FEViewport* Viewport = ENGINE.GetViewport(ViewportID); + if (Viewport == nullptr) + { + LOG.Add("FECameraSystem::SetCameraViewport Viewport was not found with such ViewportID.", "FE_LOG_ECS", FE_LOG_ERROR); + return false; + } + + FECameraComponent& CameraComponent = CameraEntity->GetComponent(); + if (CameraComponent.Viewport != nullptr) + delete CameraComponent.Viewport; + CameraComponent.Viewport = Viewport; + + ViewPortToCameraEntities[Viewport->ID].push_back(CameraEntity); + + IndividualUpdate(CameraEntity, 0.0); + return true; +} + +FEViewport* FECameraSystem::GetMainCameraViewport(FEScene* Scene) const +{ + if (Scene == nullptr) + { + LOG.Add("FECameraSystem::GetMainCameraViewport Scene is nullptr.", "FE_LOG_ECS", FE_LOG_ERROR); + return nullptr; + } + + std::vector EntitiesWithCameraComponent = Scene->GetEntityIDListWithComponent(); + for (const std::string& EntityID : EntitiesWithCameraComponent) + { + FEEntity* Entity = Scene->GetEntity(EntityID); + if (Entity == nullptr || !Entity->HasComponent()) + continue; + + FECameraComponent& CameraComponent = Entity->GetComponent(); + if (CameraComponent.bIsMainCamera) + return CameraComponent.Viewport; + } + + return nullptr; +} + + +Json::Value FECameraSystem::CameraComponentToJson(FEEntity* Entity) +{ + Json::Value Root; + if (Entity == nullptr || !Entity->HasComponent()) + { + LOG.Add("FECameraSystem::CameraComponentToJson Entity is nullptr or does not have FECameraComponent", "FE_LOG_ECS", FE_LOG_WARNING); + return Root; + } + FECameraComponent& CameraComponent = Entity->GetComponent(); + + Root["bIsMainCamera"] = CameraComponent.bIsMainCamera; + + Root["ClearColor"]["R"] = CameraComponent.ClearColor.x; + Root["ClearColor"]["G"] = CameraComponent.ClearColor.y; + Root["ClearColor"]["B"] = CameraComponent.ClearColor.z; + Root["ClearColor"]["A"] = CameraComponent.ClearColor.w; + + Root["FOV"] = CameraComponent.FOV; + Root["NearPlane"] = CameraComponent.NearPlane; + Root["FarPlane"] = CameraComponent.FarPlane; + Root["AspectRatio"] = CameraComponent.AspectRatio; + Root["RenderingScale"] = CameraComponent.RenderScale; + + // *********** Gamma Correction & Exposure *********** + Root["Gamma Correction & Exposure"]["Gamma"] = CameraComponent.GetGamma(); + Root["Gamma Correction & Exposure"]["Exposure"] = CameraComponent.GetExposure(); + // *********** Anti-Aliasing(FXAA) *********** + Root["Anti-Aliasing(FXAA)"]["FXAASpanMax"] = CameraComponent.GetFXAASpanMax(); + Root["Anti-Aliasing(FXAA)"]["FXAAReduceMin"] = CameraComponent.GetFXAAReduceMin(); + Root["Anti-Aliasing(FXAA)"]["FXAAReduceMul"] = CameraComponent.GetFXAAReduceMul(); + // *********** Bloom *********** + Root["Bloom"]["Threshold Brightness"] = CameraComponent.GetBloomThreshold(); + Root["Bloom"]["Bloom Size"] = CameraComponent.GetBloomSize(); + // *********** Depth of Field *********** + Root["Depth of Field"]["Near distance"] = CameraComponent.GetDOFNearDistance(); + Root["Depth of Field"]["Far distance"] = CameraComponent.GetDOFFarDistance(); + Root["Depth of Field"]["Strength"] = CameraComponent.GetDOFStrength(); + Root["Depth of Field"]["Distance dependent strength"] = CameraComponent.GetDOFDistanceDependentStrength(); + // *********** Distance fog *********** + Root["Distance fog"]["isDistanceFog Enabled"] = CameraComponent.IsDistanceFogEnabled(); + Root["Distance fog"]["Density"] = CameraComponent.GetDistanceFogDensity(); + Root["Distance fog"]["Gradient"] = CameraComponent.GetDistanceFogGradient(); + // *********** Chromatic Aberration *********** + Root["Chromatic Aberration"]["Shift strength"] = CameraComponent.GetChromaticAberrationIntensity(); + // *********** SSAO *********** + Root["SSAO"]["isSSAO Active"] = CameraComponent.IsSSAOEnabled(); + Root["SSAO"]["Sample Count"] = CameraComponent.GetSSAOSampleCount(); + + Root["SSAO"]["Small Details"] = CameraComponent.IsSSAOSmallDetailsEnabled(); + Root["SSAO"]["Blured"] = CameraComponent.IsSSAOResultBlured(); + + Root["SSAO"]["Bias"] = CameraComponent.GetSSAOBias(); + Root["SSAO"]["Radius"] = CameraComponent.GetSSAORadius(); + Root["SSAO"]["Radius Small Details"] = CameraComponent.GetSSAORadiusSmallDetails(); + Root["SSAO"]["Small Details Weight"] = CameraComponent.GetSSAOSmallDetailsWeight(); + + return Root; +} + +void FECameraSystem::CameraComponentFromJson(FEEntity* Entity, Json::Value Root) +{ + if (Entity == nullptr) + { + LOG.Add("FECameraSystem::CameraComponentFromJson Entity is nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + + Entity->AddComponent(); + FECameraComponent& CameraComponent = Entity->GetComponent(); + + CameraComponent.bIsMainCamera = Root["bIsMainCamera"].asBool(); + + CameraComponent.ClearColor.x = Root["ClearColor"]["R"].asFloat(); + CameraComponent.ClearColor.y = Root["ClearColor"]["G"].asFloat(); + CameraComponent.ClearColor.z = Root["ClearColor"]["B"].asFloat(); + CameraComponent.ClearColor.w = Root["ClearColor"]["A"].asFloat(); + + CameraComponent.FOV = Root["FOV"].asFloat(); + CameraComponent.NearPlane = Root["NearPlane"].asFloat(); + CameraComponent.FarPlane = Root["FarPlane"].asFloat(); + CameraComponent.AspectRatio = Root["AspectRatio"].asFloat(); + if (Root.isMember("RenderingScale")) + CameraComponent.RenderScale = Root["RenderingScale"].asFloat(); + + // *********** Gamma Correction & Exposure *********** + CameraComponent.SetGamma(Root["Gamma Correction & Exposure"]["Gamma"].asFloat()); + CameraComponent.SetExposure(Root["Gamma Correction & Exposure"]["Exposure"].asFloat()); + // *********** Anti-Aliasing(FXAA) *********** + CameraComponent.SetFXAASpanMax(Root["Anti-Aliasing(FXAA)"]["FXAASpanMax"].asFloat()); + CameraComponent.SetFXAAReduceMin(Root["Anti-Aliasing(FXAA)"]["FXAAReduceMin"].asFloat()); + CameraComponent.SetFXAAReduceMul(Root["Anti-Aliasing(FXAA)"]["FXAAReduceMul"].asFloat()); + // *********** Bloom *********** + CameraComponent.SetBloomThreshold(Root["Bloom"]["Threshold Brightness"].asFloat()); + CameraComponent.SetBloomSize(Root["Bloom"]["Bloom Size"].asFloat()); + // *********** Depth of Field *********** + CameraComponent.SetDOFNearDistance(Root["Depth of Field"]["Near distance"].asFloat()); + CameraComponent.SetDOFFarDistance(Root["Depth of Field"]["Far distance"].asFloat()); + CameraComponent.SetDOFStrength(Root["Depth of Field"]["Strength"].asFloat()); + CameraComponent.SetDOFDistanceDependentStrength(Root["Depth of Field"]["Distance dependent strength"].asFloat()); + // *********** Distance fog *********** + CameraComponent.SetDistanceFogEnabled(Root["Distance fog"]["isDistanceFog Enabled"].asBool()); + CameraComponent.SetDistanceFogDensity(Root["Distance fog"]["Density"].asFloat()); + CameraComponent.SetDistanceFogGradient(Root["Distance fog"]["Gradient"].asFloat()); + // *********** Chromatic Aberration *********** + CameraComponent.SetChromaticAberrationIntensity(Root["Chromatic Aberration"]["Shift strength"].asFloat()); + // *********** SSAO *********** + CameraComponent.SetSSAOEnabled(Root["SSAO"]["isSSAO Active"].asBool()); + CameraComponent.SetSSAOSampleCount(Root["SSAO"]["Sample Count"].asInt()); + + CameraComponent.SetSSAOSmallDetailsEnabled(Root["SSAO"]["Small Details"].asBool()); + CameraComponent.SetSSAOResultBlured(Root["SSAO"]["Blured"].asBool()); + + CameraComponent.SetSSAOBias(Root["SSAO"]["Bias"].asFloat()); + CameraComponent.SetSSAORadius(Root["SSAO"]["Radius"].asFloat()); + + CameraComponent.SetSSAORadiusSmallDetails(Root["SSAO"]["Radius Small Details"].asFloat()); + CameraComponent.SetSSAOSmallDetailsWeight(Root["SSAO"]["Small Details Weight"].asFloat()); +} + +bool FECameraSystem::SetCameraRenderingPipeline(FEEntity* CameraEntity, FERenderingPipeline NewPipeline) +{ + if (CameraEntity == nullptr || !CameraEntity->HasComponent()) + { + LOG.Add("FECameraSystem::SetCameraRenderingPipeline CameraEntity is nullptr or does not have a camera component.", "FE_LOG_ECS", FE_LOG_ERROR); + return false; + } + + FECameraComponent& CameraComponent = CameraEntity->GetComponent(); + CameraComponent.RenderingPipeline = NewPipeline; + RENDERER.ForceCameraRenderingDataUpdate(CameraEntity); + + return true; +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FECameraSystem.h b/SubSystems/Scene/Components/Systems/FECameraSystem.h new file mode 100644 index 0000000..2c7115b --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FECameraSystem.h @@ -0,0 +1,49 @@ +#pragma once +#include "../../Scene/FESceneManager.h" + +namespace FocalEngine +{ + class FOCAL_ENGINE_API FECameraSystem + { + friend class FEScene; + friend class FERenderer; + friend class FEngine; + + SINGLETON_PRIVATE_PART(FECameraSystem) + + static void OnMyComponentAdded(FEEntity* Entity); + static void OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing); + void RegisterOnComponentCallbacks(); + + static void DuplicateCameraComponent(FEEntity* EntityWithCameraComponent, FEEntity* NewEntity); + + void Update(const double DeltaTime); + + std::unordered_map> ViewPortToCameraEntities; + static void OnViewportResize(std::string ViewportID); + + static Json::Value CameraComponentToJson(FEEntity* Entity); + static void CameraComponentFromJson(FEEntity* Entity, Json::Value Root); + public: + SINGLETON_PUBLIC_PART(FECameraSystem) + + void SetMainCamera(FEEntity* CameraEntity); + FEEntity* GetMainCamera(FEScene* Scene) const; + FEViewport* GetMainCameraViewport(FEScene* Scene) const; + + bool SetCameraViewport(FEEntity* CameraEntity, std::string ViewportID); + + void IndividualUpdate(FEEntity* CameraEntity, const double DeltaTime); + + bool SetCameraRenderingPipeline(FEEntity* CameraEntity, FERenderingPipeline NewPipeline); + + bool SetCameraRenderScale(FEEntity* CameraEntity, float NewValue); + }; + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetCameraSystem(); + #define CAMERA_SYSTEM (*static_cast(GetCameraSystem())) +#else + #define CAMERA_SYSTEM FECameraSystem::GetInstance() +#endif +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FEComponentSystems.h b/SubSystems/Scene/Components/Systems/FEComponentSystems.h new file mode 100644 index 0000000..9146aea --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FEComponentSystems.h @@ -0,0 +1,11 @@ +#pragma once + +#include "FETransformSystem.h" +#include "FELightSystem.h" +#include "FECameraSystem.h" +#include "FEInstancedSystem.h" +#include "FETerrainSystem.h" +#include "FESkyDomeSystem.h" +#include "FEPrefabInstanceSystem.h" +#include "FEVirtualUISystem.h" +#include "../NativeScriptSystem/FENativeScriptProject.h" \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FEInstancedSystem.cpp b/SubSystems/Scene/Components/Systems/FEInstancedSystem.cpp new file mode 100644 index 0000000..85ace82 --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FEInstancedSystem.cpp @@ -0,0 +1,1405 @@ +#include "FEInstancedSystem.h" +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetInstancedSystem() +{ + return FEInstancedSystem::GetInstancePointer(); +} +#endif + +FEInstancedSystem::FEInstancedSystem() +{ + RegisterOnComponentCallbacks(); + COMPONENTS_TOOL.RegisterComponentToJsonFunction(InstanceComponentToJson); + COMPONENTS_TOOL.RegisterComponentFromJsonFunction(InstanceComponentFromJson); + COMPONENTS_TOOL.RegisterComponentDuplicateFunction(DuplicateInstancedComponent); +} + +void FEInstancedSystem::RegisterOnComponentCallbacks() +{ + SCENE_MANAGER.RegisterOnComponentConstructCallback(OnMyComponentAdded); + SCENE_MANAGER.RegisterOnComponentDestroyCallback(OnMyComponentDestroy); +} + +void FEInstancedSystem::OnMyComponentAdded(FEEntity* Entity) +{ + if (INSTANCED_RENDERING_SYSTEM.bInternalAdd) + return; + + if (Entity == nullptr || !Entity->HasComponent()) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + { + LOG.Add("FEInstancedComponent should be added to entity with FEGameModelComponent or FEPrefabInstanceComponent", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } + + INSTANCED_RENDERING_SYSTEM.InitializeBuffers(Entity); +} + +// There should be third system to manage that. +#include "../SubSystems/Scene/Components/Systems/FETerrainSystem.h" +void FEInstancedSystem::OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing) +{ + if (bIsSceneClearing) + return; + + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + if (InstancedComponent.TerrainToSnap != nullptr) + { + TERRAIN_SYSTEM.UnSnapInstancedEntity(InstancedComponent.TerrainToSnap, Entity); + } +} + +FEInstancedSystem::~FEInstancedSystem() {}; + +void FEInstancedSystem::InitializeBuffers(FEEntity* Entity) +{ + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + size_t CurrentBufferIndex = 0; + if (Entity->HasComponent()) + { + FEPrefabInstanceComponent& PrefabInstanceComponent = Entity->GetComponent(); + FEScene* PrefabScene = PrefabInstanceComponent.GetPrefab()->GetScene(); + + std::vector AllPrefabEntities = PrefabScene->GetEntityIDListWithComponent(); + for (size_t i = 0; i < AllPrefabEntities.size(); i++) + { + FEEntity* CurrentPrefabEntity = PrefabScene->GetEntity(AllPrefabEntities[i]); + if (CurrentPrefabEntity != nullptr) + { + FEGameModelComponent& GameModelComponent = CurrentPrefabEntity->GetComponent(); + FEInstancedElementData* NewData = new FEInstancedElementData(); + InstancedComponent.InstancedElementsData.push_back(NewData); + + InstancedComponent.InstancedElementsData[CurrentBufferIndex]->EntityIDWithGameModelComponent = CurrentPrefabEntity->GetObjectID(); + InitializeBuffer(Entity, GameModelComponent, CurrentBufferIndex); + CurrentBufferIndex++; + } + } + } + else + { + FEInstancedElementData* NewData = new FEInstancedElementData(); + InstancedComponent.InstancedElementsData.push_back(NewData); + + InstancedComponent.InstancedElementsData[CurrentBufferIndex]->EntityIDWithGameModelComponent = Entity->GetObjectID(); + InitializeBuffer(Entity, Entity->GetComponent(), CurrentBufferIndex); + } +} + +void FEInstancedSystem::InitializeBuffer(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex) +{ + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + InstancedComponent.InstancedElementsData[BufferIndex]->LODCounts = new int[GameModelComponent.GetGameModel()->GetMaxLODCount()]; + + InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers = new GLenum[GameModelComponent.GetGameModel()->GetMaxLODCount()]; + for (size_t i = 0; i < GameModelComponent.GetGameModel()->GetMaxLODCount(); i++) + { + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[i])); + } + + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->SourceDataBuffer)); + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->PositionsBuffer)); + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->AABBSizesBuffer)); + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->LODInfoBuffer)); + + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo = new FEDrawElementsIndirectCommand[4]; + for (size_t i = 0; i < GameModelComponent.GetGameModel()->GetMaxLODCount(); i++) + { + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo[i].Count = GameModelComponent.GetGameModel()->GetLODMesh(i) == nullptr ? 0 : GameModelComponent.GetGameModel()->GetLODMesh(i)->GetVertexCount(); + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo[i].BaseInstance = 0; + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo[i].BaseVertex = 0; + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo[i].FirstIndex = 0; + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo[i].PrimCount = 0; + } + + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawInfoBuffer)); + FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawInfoBuffer)); + FE_GL_ERROR(glBufferStorage(GL_DRAW_INDIRECT_BUFFER, sizeof(FEDrawElementsIndirectCommand) * 4, InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo, GL_MAP_READ_BIT)); + FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0)); + + InstancedComponent.InstancedElementsData[BufferIndex]->InstancedMatricesLOD.resize(GameModelComponent.GetGameModel()->GetMaxLODCount()); +} + +void FEInstancedSystem::InitializeGPUCullingBuffers(FEEntity* Entity) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + size_t CurrentBufferIndex = 0; + if (Entity->HasComponent()) + { + FEPrefabInstanceComponent& PrefabInstanceComponent = Entity->GetComponent(); + FEScene* PrefabScene = PrefabInstanceComponent.GetPrefab()->GetScene(); + + std::vector AllPrefabEntities = PrefabScene->GetEntityIDListWithComponent(); + for (size_t i = 0; i < AllPrefabEntities.size(); i++) + { + FEEntity* CurrentPrefabEntity = PrefabScene->GetEntity(AllPrefabEntities[i]); + if (CurrentPrefabEntity != nullptr) + { + FEGameModelComponent& GameModelComponent = CurrentPrefabEntity->GetComponent(); + InitializeGPUCullingBuffer(Entity, GameModelComponent, CurrentBufferIndex); + CurrentBufferIndex++; + } + } + } + else + { + InitializeGPUCullingBuffer(Entity, Entity->GetComponent(), CurrentBufferIndex); + } +} + +void FEInstancedSystem::InitializeGPUCullingBuffer(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex) +{ + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + if (InstancedComponent.InstancedElementsData[BufferIndex]->SourceDataBuffer != 0) + { + FE_GL_ERROR(glDeleteBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->SourceDataBuffer)); + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->SourceDataBuffer)); + } + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, InstancedComponent.InstancedElementsData[BufferIndex]->SourceDataBuffer)); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstancedComponent.InstanceCount * sizeof(glm::mat4), InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices.data(), GL_DYNAMIC_DRAW)); + + if (InstancedComponent.InstancedElementsData[BufferIndex]->PositionsBuffer != 0) + { + FE_GL_ERROR(glDeleteBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->PositionsBuffer)); + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->PositionsBuffer)); + } + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, InstancedComponent.InstancedElementsData[BufferIndex]->PositionsBuffer)); + + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstancedComponent.InstanceCount * sizeof(float) * 3, InstancedComponent.InstancedElementsData[BufferIndex]->InstancePositions.data(), GL_DYNAMIC_DRAW)); + + if (InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[0] != 0) + { + FE_GL_ERROR(glDeleteBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[0])); + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[0])); + } + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[0])); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstancedComponent.InstanceCount * sizeof(glm::mat4), nullptr, GL_DYNAMIC_DRAW)); + + if (InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[1] != 0) + { + FE_GL_ERROR(glDeleteBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[1])); + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[1])); + } + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[1])); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstancedComponent.InstanceCount * sizeof(glm::mat4), nullptr, GL_DYNAMIC_DRAW)); + + if (InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[2] != 0) + { + FE_GL_ERROR(glDeleteBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[2])); + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[2])); + } + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[2])); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstancedComponent.InstanceCount * sizeof(glm::mat4), nullptr, GL_DYNAMIC_DRAW)); + + if (InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[3] != 0) + { + FE_GL_ERROR(glDeleteBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[3])); + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[3])); + } + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 9, InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[3])); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstancedComponent.InstanceCount * sizeof(glm::mat4), nullptr, GL_DYNAMIC_DRAW)); + + if (InstancedComponent.InstancedElementsData[BufferIndex]->AABBSizesBuffer != 0) + { + FE_GL_ERROR(glDeleteBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->AABBSizesBuffer)); + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->AABBSizesBuffer)); + } + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, InstancedComponent.InstancedElementsData[BufferIndex]->AABBSizesBuffer)); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstancedComponent.InstanceCount * sizeof(float), InstancedComponent.InstancedElementsData[BufferIndex]->InstancedAABBSizes.data(), GL_DYNAMIC_DRAW)); + + std::vector LODInfoData; + LODInfoData.push_back(GameModelComponent.GetGameModel()->GetCullDistance()); + LODInfoData.push_back(GameModelComponent.GetGameModel()->GetLODMaxDrawDistance(0)); + LODInfoData.push_back(GameModelComponent.GetGameModel()->GetLODMaxDrawDistance(1)); + LODInfoData.push_back(GameModelComponent.GetGameModel()->GetLODMaxDrawDistance(2)); + + // does it have billboard ? + unsigned int BillboardIndex = 5; + for (size_t j = 0; j < GameModelComponent.GetGameModel()->GetMaxLODCount(); j++) + { + if (GameModelComponent.GetGameModel()->IsLODBillboard(j) && GameModelComponent.GetGameModel()->GetLODMesh(j) != nullptr) + { + BillboardIndex = static_cast(j); + } + } + + LODInfoData.push_back(static_cast(BillboardIndex)); + // This should not be here, instead normal of plane should align with vector to camera. + LODInfoData.push_back(1.5708f * 3.0f + GameModelComponent.GetGameModel()->GetBillboardZeroRotaion() * ANGLE_TORADIANS_COF); + LODInfoData.push_back(static_cast(InstancedComponent.InstanceCount)); + + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, InstancedComponent.InstancedElementsData[BufferIndex]->LODInfoBuffer)); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, 7 * sizeof(float), LODInfoData.data(), GL_DYNAMIC_DRAW)); + + if (InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo == nullptr) + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo = new FEDrawElementsIndirectCommand[4]; + + for (size_t j = 0; j < GameModelComponent.GetGameModel()->GetMaxLODCount(); j++) + { + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo[j].Count = GameModelComponent.GetGameModel()->GetLODMesh(j) == nullptr ? 0 : GameModelComponent.GetGameModel()->GetLODMesh(j)->GetVertexCount(); + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo[j].BaseInstance = 0; + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo[j].BaseVertex = 0; + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo[j].FirstIndex = 0; + InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo[j].PrimCount = 0; + } + + if (InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawInfoBuffer != 0) + { + FE_GL_ERROR(glDeleteBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawInfoBuffer)); + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawInfoBuffer)); + } + + FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawInfoBuffer)); + FE_GL_ERROR(glBufferStorage(GL_DRAW_INDIRECT_BUFFER, sizeof(FEDrawElementsIndirectCommand) * 4, InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawsInfo, GL_MAP_READ_BIT)); + FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0)); +} + +void FEInstancedSystem::UpdateBuffers(FEEntity* Entity) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + size_t CurrentBufferIndex = 0; + if (Entity->HasComponent()) + { + FEPrefabInstanceComponent& PrefabInstanceComponent = Entity->GetComponent(); + FEScene* PrefabScene = PrefabInstanceComponent.GetPrefab()->GetScene(); + + std::vector AllPrefabEntities = PrefabScene->GetEntityIDListWithComponent(); + for (size_t i = 0; i < AllPrefabEntities.size(); i++) + { + FEEntity* CurrentPrefabEntity = PrefabScene->GetEntity(AllPrefabEntities[i]); + if (CurrentPrefabEntity != nullptr) + { + FEGameModelComponent& GameModelComponent = CurrentPrefabEntity->GetComponent(); + UpdateBuffer(Entity, GameModelComponent, CurrentBufferIndex); + CurrentBufferIndex++; + } + } + } + else + { + UpdateBuffer(Entity, Entity->GetComponent(), CurrentBufferIndex); + } + + //UpdateBuffers(TransformComponent, GameModelComponent, InstancedComponent); +} + +void FEInstancedSystem::UpdateBuffer(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex) +{ + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + if (InstancedComponent.InstancedElementsData[BufferIndex]->InstancedBuffer != 0) + { + glDeleteBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->InstancedBuffer); + } + + FE_GL_ERROR(glGenBuffers(1, &InstancedComponent.InstancedElementsData[BufferIndex]->InstancedBuffer)); + FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, InstancedComponent.InstancedElementsData[BufferIndex]->InstancedBuffer)); + FE_GL_ERROR(glBufferData(GL_ARRAY_BUFFER, InstancedComponent.InstanceCount * sizeof(glm::mat4), InstancedComponent.InstancedElementsData[BufferIndex]->InstancedMatrices.data(), GL_DYNAMIC_DRAW)); + + const unsigned int VAO = GameModelComponent.GetGameModel()->Mesh->GetVaoID(); + FE_GL_ERROR(glBindVertexArray(VAO)); + // set attribute pointers for matrix (4 times vec4) + FE_GL_ERROR(glEnableVertexAttribArray(6)); + FE_GL_ERROR(glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), static_cast(nullptr))); + FE_GL_ERROR(glEnableVertexAttribArray(7)); + FE_GL_ERROR(glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)))); + FE_GL_ERROR(glEnableVertexAttribArray(8)); + FE_GL_ERROR(glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)))); + FE_GL_ERROR(glEnableVertexAttribArray(9)); + FE_GL_ERROR(glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)))); + + FE_GL_ERROR(glVertexAttribDivisor(6, 1)); + FE_GL_ERROR(glVertexAttribDivisor(7, 1)); + FE_GL_ERROR(glVertexAttribDivisor(8, 1)); + FE_GL_ERROR(glVertexAttribDivisor(9, 1)); + + FE_GL_ERROR(glBindVertexArray(0)); + + for (size_t i = 0; i < GameModelComponent.GetGameModel()->GetMaxLODCount(); i++) + { + if (GameModelComponent.GetGameModel()->GetLODMesh(i) != nullptr) + { + const unsigned int VAO = GameModelComponent.GetGameModel()->GetLODMesh(i)->GetVaoID(); + FE_GL_ERROR(glBindVertexArray(VAO)); + // set attribute pointers for matrix (4 times vec4) + FE_GL_ERROR(glEnableVertexAttribArray(6)); + FE_GL_ERROR(glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), static_cast(nullptr))); + FE_GL_ERROR(glEnableVertexAttribArray(7)); + FE_GL_ERROR(glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)))); + FE_GL_ERROR(glEnableVertexAttribArray(8)); + FE_GL_ERROR(glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)))); + FE_GL_ERROR(glEnableVertexAttribArray(9)); + FE_GL_ERROR(glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)))); + + FE_GL_ERROR(glVertexAttribDivisor(6, 1)); + FE_GL_ERROR(glVertexAttribDivisor(7, 1)); + FE_GL_ERROR(glVertexAttribDivisor(8, 1)); + FE_GL_ERROR(glVertexAttribDivisor(9, 1)); + + FE_GL_ERROR(glBindVertexArray(0)); + } + } + + FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + InstancedComponent.InstancedElementsData[BufferIndex]->AllInstancesAABB = FEAABB(); + FETransformComponent& TransformComponent = Entity->GetComponent(); + glm::vec3 Position = TransformComponent.GetPosition(); + for (size_t i = 0; i < InstancedComponent.InstanceCount; i++) + { + glm::mat4 MatWithoutTranslate = InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices[i]; + MatWithoutTranslate[3][0] -= Position.x; + MatWithoutTranslate[3][1] -= Position.y; + MatWithoutTranslate[3][2] -= Position.z; + + InstancedComponent.InstancedElementsData[BufferIndex]->AllInstancesAABB = InstancedComponent.InstancedElementsData[BufferIndex]->AllInstancesAABB.Merge(GameModelComponent.GetGameModel()->Mesh->AABB.Transform(MatWithoutTranslate)); + } + + TransformComponent.SetDirtyFlag(true); + GetAABB(Entity); +} + +void FEInstancedSystem::DuplicateInstancedComponent(FEEntity* SourceEntity, FEEntity* TargetEntity) +{ + if (SourceEntity == nullptr || TargetEntity == nullptr) + return; + + if (!SourceEntity->HasComponent() || !TargetEntity->HasComponent()) + return; + + if (!SourceEntity->HasComponent()) + return; + + FEInstancedComponent& OriginalInstancedComponent = SourceEntity->GetComponent(); + INSTANCED_RENDERING_SYSTEM.bInternalAdd = true; + TargetEntity->AddComponent(); + INSTANCED_RENDERING_SYSTEM.bInternalAdd = false; + + FEInstancedComponent& NewInstancedComponent = TargetEntity->GetComponent(); + // Copy all data from source entity to target entity. + NewInstancedComponent = OriginalInstancedComponent; + // To allocate new buffers for target entity. + NewInstancedComponent.InstancedElementsData.clear(); + INSTANCED_RENDERING_SYSTEM.InitializeBuffers(TargetEntity); + + // After InitializeBuffers some data still needs to be copied. + for (size_t i = 0; i < OriginalInstancedComponent.InstancedElementsData.size(); i++) + { + NewInstancedComponent.InstancedElementsData[i]->InstancedAABBSizes = OriginalInstancedComponent.InstancedElementsData[i]->InstancedAABBSizes; + NewInstancedComponent.InstancedElementsData[i]->InstancedMatrices = OriginalInstancedComponent.InstancedElementsData[i]->InstancedMatrices; + NewInstancedComponent.InstancedElementsData[i]->TransformedInstancedMatrices = OriginalInstancedComponent.InstancedElementsData[i]->TransformedInstancedMatrices; + NewInstancedComponent.InstancedElementsData[i]->InstancePositions = OriginalInstancedComponent.InstancedElementsData[i]->InstancePositions; + NewInstancedComponent.InstancedElementsData[i]->InstancedMatricesLOD = OriginalInstancedComponent.InstancedElementsData[i]->InstancedMatricesLOD; + + NewInstancedComponent.InstancedElementsData[i]->EntityIDWithGameModelComponent = TargetEntity->GetObjectID(); + } +} + +void FEInstancedSystem::AddInstanceInternal(FEEntity* Entity, const glm::mat4 InstanceMatrix) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + for (size_t i = 0; i < InstancedComponent.InstancedElementsData.size(); i++) + { + FEEntity* EntityWithGameModel = GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[i]->EntityIDWithGameModelComponent); + if (EntityWithGameModel == nullptr) + continue; + + FEGameModelComponent& GameModelComponent = EntityWithGameModel->GetComponent(); + AddInstanceInternal(Entity, GameModelComponent, InstanceMatrix, i); + } + + InstancedComponent.InstanceCount++; + InstancedComponent.bDirtyFlag = true; +} + +void FEInstancedSystem::AddInstanceInternal(FEEntity* Entity, FEGameModelComponent& GameModelComponent, glm::mat4 InstanceMatrix, size_t BufferIndex) +{ + FETransformComponent& TransformComponent = Entity->GetComponent(); + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + InstancedComponent.InstancedElementsData[BufferIndex]->InstancedAABBSizes.push_back(-FEAABB(GameModelComponent.GetGameModel()->GetMesh()->GetAABB(), InstanceMatrix).GetLongestAxisLength()); + InstancedComponent.InstancedElementsData[BufferIndex]->InstancedMatrices.push_back(InstanceMatrix); + InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices.push_back(TransformComponent.GetWorldMatrix() * InstanceMatrix); + InstancedComponent.InstancedElementsData[BufferIndex]->InstancePositions.push_back(InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices.back()[3]); + + for (size_t j = 0; j < GameModelComponent.GetGameModel()->GetMaxLODCount(); j++) + { + InstancedComponent.InstancedElementsData[BufferIndex]->InstancedMatricesLOD[j].resize(InstancedComponent.InstanceCount); + } +} + +void FEInstancedSystem::AddInstances(FEEntity* Entity, const glm::mat4* InstanceMatrix, size_t Count) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + InstancedComponent.InstanceCount += Count; + for (size_t i = 0; i < InstancedComponent.InstancedElementsData.size(); i++) + { + FEEntity* EntityWithGameModel = GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[i]->EntityIDWithGameModelComponent); + if (EntityWithGameModel == nullptr) + continue; + + FEGameModelComponent& GameModelComponent = EntityWithGameModel->GetComponent(); + AddInstances(Entity, GameModelComponent, InstanceMatrix, Count, i); + } + + InstancedComponent.bDirtyFlag = true; +} + +void FEInstancedSystem::AddInstances(FEEntity* Entity, FEGameModelComponent& GameModelComponent, const glm::mat4* InstanceMatrix, size_t Count, size_t BufferIndex) +{ + FETransformComponent& TransformComponent = Entity->GetComponent(); + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + const size_t StartIndex = InstancedComponent.InstancedElementsData[BufferIndex]->InstancedAABBSizes.size(); + + InstancedComponent.InstancedElementsData[BufferIndex]->InstancedAABBSizes.resize(InstancedComponent.InstancedElementsData[BufferIndex]->InstancedAABBSizes.size() + Count); + const FEAABB OriginalAABB = GameModelComponent.GetGameModel()->GetMesh()->GetAABB(); + InstancedComponent.InstancedElementsData[BufferIndex]->InstancedMatrices.resize(InstancedComponent.InstancedElementsData[BufferIndex]->InstancedMatrices.size() + Count); + InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices.resize(InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices.size() + Count); + InstancedComponent.InstancedElementsData[BufferIndex]->InstancePositions.resize(InstancedComponent.InstancedElementsData[BufferIndex]->InstancePositions.size() + Count); + + for (size_t j = StartIndex; j < Count; j++) + { + InstancedComponent.InstancedElementsData[BufferIndex]->InstancedAABBSizes[j] = -FEAABB(OriginalAABB, InstanceMatrix[j]).GetLongestAxisLength(); + InstancedComponent.InstancedElementsData[BufferIndex]->InstancedMatrices[j] = InstanceMatrix[j]; + InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices[j] = TransformComponent.GetWorldMatrix() * InstanceMatrix[j]; + + InstancedComponent.InstancedElementsData[BufferIndex]->InstancePositions[j] = InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices[j][3]; + } + + for (size_t j = 0; j < GameModelComponent.GetGameModel()->GetMaxLODCount(); j++) + { + InstancedComponent.InstancedElementsData[BufferIndex]->InstancedMatricesLOD[j].resize(InstancedComponent.InstanceCount); + } +} + +FEAABB FEInstancedSystem::GetAABB(FEEntity* Entity) +{ + if (Entity == nullptr) + return FEAABB(); + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return FEAABB(); + + FETransformComponent& TransformComponent = Entity->GetComponent(); + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + FEAABB Result = FEAABB(); + for (size_t i = 0; i < InstancedComponent.InstancedElementsData.size(); i++) + { + Result = Result.Merge(InstancedComponent.InstancedElementsData[i]->AllInstancesAABB.Transform(TransformComponent.GetWorldMatrix())); + } + + if (TransformComponent.IsDirty()) + { + UpdateMatrices(Entity); + TransformComponent.SetDirtyFlag(false); + } + + return Result; +} + +void FEInstancedSystem::UpdateMatrices(FEEntity* Entity) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + size_t CurrentBufferIndex = 0; + if (Entity->HasComponent()) + { + FEPrefabInstanceComponent& PrefabInstanceComponent = Entity->GetComponent(); + FEScene* PrefabScene = PrefabInstanceComponent.GetPrefab()->GetScene(); + + std::vector AllPrefabEntities = PrefabScene->GetEntityIDListWithComponent(); + for (size_t i = 0; i < AllPrefabEntities.size(); i++) + { + FEEntity* CurrentPrefabEntity = PrefabScene->GetEntity(AllPrefabEntities[i]); + if (CurrentPrefabEntity != nullptr) + { + UpdateMatrix(Entity, CurrentBufferIndex); + CurrentBufferIndex++; + } + } + } + else + { + UpdateMatrix(Entity, CurrentBufferIndex); + } +} + +void FEInstancedSystem::UpdateMatrix(FEEntity* Entity, size_t BufferIndex) +{ + FETransformComponent& TransformComponent = Entity->GetComponent(); + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + if (InstancedComponent.InstancedElementsData[BufferIndex]->InstancedMatrices.size() != InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices.size()) + { + LOG.Add("InstancedMatrices size and TransformedInstancedMatrices size is not equal in FEInstancedSystem::UpdateMatrix", "FE_LOG_RENDERING", FE_LOG_ERROR); + return; + } + + for (size_t i = 0; i < InstancedComponent.InstancedElementsData[BufferIndex]->InstancedMatrices.size(); i++) + { + InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices[i] = TransformComponent.GetWorldMatrix() * InstancedComponent.InstancedElementsData[BufferIndex]->InstancedMatrices[i]; + InstancedComponent.InstancedElementsData[BufferIndex]->InstancePositions[i] = InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices[i][3]; + } + + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, InstancedComponent.InstancedElementsData[BufferIndex]->SourceDataBuffer)); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstancedComponent.InstanceCount * sizeof(glm::mat4), InstancedComponent.InstancedElementsData[BufferIndex]->TransformedInstancedMatrices.data(), GL_DYNAMIC_DRAW)); + + FE_GL_ERROR(glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, InstancedComponent.InstancedElementsData[BufferIndex]->PositionsBuffer)); + FE_GL_ERROR(glBufferData(GL_SHADER_STORAGE_BUFFER, InstancedComponent.InstanceCount * sizeof(float) * 3, InstancedComponent.InstancedElementsData[BufferIndex]->InstancePositions.data(), GL_DYNAMIC_DRAW)); +} + +void FEInstancedSystem::Render(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + if (InstancedComponent.InstanceCount == 0) + return; + + CheckDirtyFlag(Entity); + + RenderGameModelComponent(GameModelComponent, InstancedComponent, BufferIndex); +} + +void FEInstancedSystem::RenderGameModelComponent(FEGameModelComponent& GameModelComponent, FEInstancedComponent& InstancedComponent, size_t BufferIndex) +{ + for (size_t i = 0; i < GameModelComponent.GetGameModel()->GetMaxLODCount(); i++) + { + if (GameModelComponent.GetGameModel()->IsLODBillboard(i)) + break; + + if (GameModelComponent.GetGameModel()->GetLODMesh(i) != nullptr) + { + if (InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[i] == 0) + break; + + FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[i])); + + FE_GL_ERROR(glBindVertexArray(GameModelComponent.GetGameModel()->GetLODMesh(i)->GetVaoID())); + + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); + + FE_GL_ERROR(glEnableVertexAttribArray(6)); + FE_GL_ERROR(glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), static_cast(0))); + FE_GL_ERROR(glEnableVertexAttribArray(7)); + FE_GL_ERROR(glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)))); + FE_GL_ERROR(glEnableVertexAttribArray(8)); + FE_GL_ERROR(glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)))); + FE_GL_ERROR(glEnableVertexAttribArray(9)); + FE_GL_ERROR(glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)))); + + FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawInfoBuffer)); + FE_GL_ERROR(glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, (GLvoid*)(i * sizeof(FEDrawElementsIndirectCommand)))); + + FE_GL_ERROR(glBindVertexArray(0)); + FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, 0)); + } + } + + if (InstancedComponent.CullingType == FE_CULLING_NONE) + { + FE_GL_ERROR(glBindVertexArray(GameModelComponent.GetGameModel()->Mesh->GetVaoID())); + if ((GameModelComponent.GetGameModel()->Mesh->VertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); + if ((GameModelComponent.GetGameModel()->Mesh->VertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); + if ((GameModelComponent.GetGameModel()->Mesh->VertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); + if ((GameModelComponent.GetGameModel()->Mesh->VertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); + if ((GameModelComponent.GetGameModel()->Mesh->VertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); + if ((GameModelComponent.GetGameModel()->Mesh->VertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); + + FE_GL_ERROR(glEnableVertexAttribArray(6)); + FE_GL_ERROR(glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), static_cast(nullptr))); + FE_GL_ERROR(glEnableVertexAttribArray(7)); + FE_GL_ERROR(glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)))); + FE_GL_ERROR(glEnableVertexAttribArray(8)); + FE_GL_ERROR(glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)))); + FE_GL_ERROR(glEnableVertexAttribArray(9)); + FE_GL_ERROR(glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)))); + + FE_GL_ERROR(glDrawElementsInstanced(GL_TRIANGLES, GameModelComponent.GetGameModel()->Mesh->GetVertexCount(), GL_UNSIGNED_INT, nullptr, static_cast(InstancedComponent.InstanceCount))); + + FE_GL_ERROR(glBindVertexArray(0)); + } +} + +void FEInstancedSystem::RenderOnlyBillbords(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + RenderOnlyBillbordsInternal(Entity, GameModelComponent, BufferIndex); +} + +void FEInstancedSystem::RenderOnlyBillbordsInternal(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex) +{ + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + for (size_t i = 0; i < GameModelComponent.GetGameModel()->GetMaxLODCount(); i++) + { + if (GameModelComponent.GetGameModel()->IsLODBillboard(i) && GameModelComponent.GetGameModel()->GetLODMesh(i) != nullptr) + { + if (InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[i] == 0) + break; + + FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, InstancedComponent.InstancedElementsData[BufferIndex]->LODBuffers[i])); + + FE_GL_ERROR(glBindVertexArray(GameModelComponent.GetGameModel()->GetLODMesh(i)->GetVaoID())); + + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_POSITION) == FE_POSITION) FE_GL_ERROR(glEnableVertexAttribArray(0)); + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_COLOR) == FE_COLOR) FE_GL_ERROR(glEnableVertexAttribArray(1)); + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_NORMAL) == FE_NORMAL) FE_GL_ERROR(glEnableVertexAttribArray(2)); + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_TANGENTS) == FE_TANGENTS) FE_GL_ERROR(glEnableVertexAttribArray(3)); + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_UV) == FE_UV) FE_GL_ERROR(glEnableVertexAttribArray(4)); + if ((GameModelComponent.GetGameModel()->GetLODMesh(i)->VertexAttributes & FE_MATINDEX) == FE_MATINDEX) FE_GL_ERROR(glEnableVertexAttribArray(5)); + + FE_GL_ERROR(glEnableVertexAttribArray(6)); + FE_GL_ERROR(glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), static_cast(nullptr))); + FE_GL_ERROR(glEnableVertexAttribArray(7)); + FE_GL_ERROR(glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)))); + FE_GL_ERROR(glEnableVertexAttribArray(8)); + FE_GL_ERROR(glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)))); + FE_GL_ERROR(glEnableVertexAttribArray(9)); + FE_GL_ERROR(glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)))); + + FE_GL_ERROR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, InstancedComponent.InstancedElementsData[BufferIndex]->IndirectDrawInfoBuffer)); + FE_GL_ERROR(glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, (GLvoid*)(i * sizeof(FEDrawElementsIndirectCommand)))); + + FE_GL_ERROR(glBindVertexArray(0)); + FE_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + break; + } + } +} + +void FEInstancedSystem::AddIndividualInstance(FEEntity* Entity, glm::mat4 InstanceMatrix) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + FETransformComponent& TransformComponent = Entity->GetComponent(); + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + for (size_t i = 0; i < InstancedComponent.InstancedElementsData.size(); i++) + { + FEEntity* EntityWithGameModel = GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[i]->EntityIDWithGameModelComponent); + if (EntityWithGameModel == nullptr) + continue; + + FEGameModelComponent& GameModelComponent = EntityWithGameModel->GetComponent(); + AddInstanceInternal(Entity, GameModelComponent, glm::inverse(TransformComponent.GetWorldMatrix()) * InstanceMatrix, i); + + if (InstancedComponent.IndividualInstancedAABB.empty()) + { + UpdateIndividualSelectModeAABBData(GameModelComponent, InstancedComponent); + } + else + { + FEAABB NewAABB; + for (size_t j = 0; j < InstancedComponent.InstancedElementsData.size(); j++) + { + FEEntity* EntityWithGameModel = GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[j]->EntityIDWithGameModelComponent); + if (EntityWithGameModel == nullptr) + continue; + + FEGameModelComponent& LocalGameModelComponent = EntityWithGameModel->GetComponent(); + + NewAABB = NewAABB.Merge(FEAABB(LocalGameModelComponent.GetGameModel()->GetMesh()->GetAABB(), InstancedComponent.InstancedElementsData[j]->TransformedInstancedMatrices.back())); + } + + InstancedComponent.IndividualInstancedAABB.push_back(NewAABB/*FEAABB(GameModelComponent.GetGameModel()->GetMesh()->GetAABB(), InstancedComponent.TransformedInstancedMatrices.back())*/); + } + } + + InstancedComponent.Modifications.push_back(FEInstanceModification(FE_CHANGE_ADDED, static_cast(InstancedComponent.InstanceCount), InstanceMatrix)); + InstancedComponent.bDirtyFlag = true; +} + +// Not elegant solution, I need third system to handle this. +#include "../SubSystems/Scene/Components/Systems/FETerrainSystem.h" +bool FEInstancedSystem::TryToSnapIndividualInstance(FEEntity* Entity, size_t InstanceIndex) +{ + if (Entity == nullptr) + return false; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return false; + + FETransformComponent& TransformComponent = Entity->GetComponent(); + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + FEGameModelComponent& GameModelComponent = GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[0]->EntityIDWithGameModelComponent)->GetComponent(); + + if (InstanceIndex < 0 || InstanceIndex >= InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices.size() || InstancedComponent.TerrainToSnap == nullptr) + return false; + + if (!IsIndividualSelectMode(InstancedComponent)) + return false; + + const float Y = TERRAIN_SYSTEM.GetHeightAt(InstancedComponent.TerrainToSnap, glm::vec2(InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices[InstanceIndex][3][0], InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices[InstanceIndex][3][2])); + if (Y == -FLT_MAX) + return false; + + if (InstancedComponent.TerrainLayer != -1) + { + FETerrainComponent& TerrainComponent = InstancedComponent.TerrainToSnap->GetComponent(); + const float LayerIntensity = TERRAIN_SYSTEM.GetLayerIntensityAt(InstancedComponent.TerrainToSnap, glm::vec2(InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices[InstanceIndex][3][0], InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices[InstanceIndex][3][2]), InstancedComponent.TerrainLayer); + if (LayerIntensity < InstancedComponent.MinLayerIntensityToSpawn) + return false; + } + + if (abs(InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices[InstanceIndex][3][1] - Y) < 0.01f) + return true; + + glm::mat4 Copy = InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices[InstanceIndex]; + Copy[3][1] = Y; + ModifyIndividualInstance(Entity, InstanceIndex, Copy); + InstancedComponent.bDirtyFlag = true; + return true; +} + +void FEInstancedSystem::DeleteIndividualInstance(FEEntity* Entity, const size_t InstanceIndex) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + if (InstanceIndex < 0 || InstanceIndex >= InstancedComponent.InstancedElementsData[0]->InstancedMatrices.size()) + return; + + InstancedComponent.Modifications.push_back(FEInstanceModification(FE_CHANGE_DELETED, static_cast(InstanceIndex), glm::mat4())); + InstancedComponent.InstanceCount--; + + + + for (size_t i = 0; i < InstancedComponent.InstancedElementsData.size(); i++) + { + FEEntity* EntityWithGameModel = GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[i]->EntityIDWithGameModelComponent); + if (EntityWithGameModel == nullptr) + continue; + + FEGameModelComponent& GameModelComponent = EntityWithGameModel->GetComponent(); + + InstancedComponent.InstancedElementsData[i]->InstancedAABBSizes.erase(InstancedComponent.InstancedElementsData[i]->InstancedAABBSizes.begin() + InstanceIndex); + InstancedComponent.InstancedElementsData[i]->InstancedMatrices.erase(InstancedComponent.InstancedElementsData[i]->InstancedMatrices.begin() + InstanceIndex); + InstancedComponent.InstancedElementsData[i]->TransformedInstancedMatrices.erase(InstancedComponent.InstancedElementsData[i]->TransformedInstancedMatrices.begin() + InstanceIndex); + InstancedComponent.InstancedElementsData[i]->InstancePositions.erase(InstancedComponent.InstancedElementsData[i]->InstancePositions.begin() + InstanceIndex); + + for (size_t j = 0; j < GameModelComponent.GetGameModel()->GetMaxLODCount(); j++) + { + InstancedComponent.InstancedElementsData[i]->InstancedMatricesLOD[j].resize(InstancedComponent.InstanceCount); + } + } + + if (InstancedComponent.IndividualInstancedAABB.empty()) + { + UpdateIndividualSelectModeAABBData(Entity); + } + else + { + InstancedComponent.IndividualInstancedAABB.erase(InstancedComponent.IndividualInstancedAABB.begin() + InstanceIndex); + } + + InstancedComponent.bDirtyFlag = true; +} + +void FEInstancedSystem::ModifyIndividualInstance(FEEntity* Entity, const size_t InstanceIndex, glm::mat4 NewMatrix) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + FETransformComponent& TransformComponent = Entity->GetComponent(); + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + if (InstanceIndex < 0 || InstanceIndex >= InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices.size()) + return; + + if (glm::all(glm::epsilonEqual(InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices[InstanceIndex][0], NewMatrix[0], 0.001f)) && + glm::all(glm::epsilonEqual(InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices[InstanceIndex][1], NewMatrix[1], 0.001f)) && + glm::all(glm::epsilonEqual(InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices[InstanceIndex][2], NewMatrix[2], 0.001f)) && + glm::all(glm::epsilonEqual(InstancedComponent.InstancedElementsData[0]->TransformedInstancedMatrices[InstanceIndex][3], NewMatrix[3], 0.001f))) + return; + + if (!InstancedComponent.Modifications.empty() && InstancedComponent.Modifications.back().Index == InstanceIndex && InstancedComponent.Modifications.back().Type == FE_CHANGE_MODIFIED) + { + InstancedComponent.Modifications.back().Modification = NewMatrix; + } + else + { + InstancedComponent.Modifications.push_back(FEInstanceModification(FE_CHANGE_MODIFIED, static_cast(InstanceIndex), NewMatrix)); + } + + for (size_t i = 0; i < InstancedComponent.InstancedElementsData.size(); i++) + { + FEEntity* EntityWithGameModel = GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[i]->EntityIDWithGameModelComponent); + if (EntityWithGameModel == nullptr) + continue; + + FEGameModelComponent& GameModelComponent = EntityWithGameModel->GetComponent(); + + InstancedComponent.InstancedElementsData[i]->TransformedInstancedMatrices[InstanceIndex] = NewMatrix; + InstancedComponent.InstancedElementsData[i]->InstancedMatrices[InstanceIndex] = glm::inverse(TransformComponent.GetWorldMatrix()) * NewMatrix; + + if (InstancedComponent.IndividualInstancedAABB.size() > InstanceIndex) + InstancedComponent.IndividualInstancedAABB[InstanceIndex] = FEAABB(GameModelComponent.GetGameModel()->GetMesh()->GetAABB(), NewMatrix); + InstancedComponent.InstancedElementsData[i]->InstancedAABBSizes[InstanceIndex] = -FEAABB(GameModelComponent.GetGameModel()->GetMesh()->GetAABB(), NewMatrix).GetLongestAxisLength(); + + } + + InstancedComponent.bDirtyFlag = true; +} + +void FEInstancedSystem::Update() +{ + for (int i = 0; i < EnitityIDListToInitialize.size(); i++) + { + std::string SceneID = EnitityIDListToInitialize[i].first; + std::string EntityID = EnitityIDListToInitialize[i].second; + + FEScene* Scene = SCENE_MANAGER.GetScene(SceneID); + if (Scene == nullptr) + { + EnitityIDListToInitialize.erase(EnitityIDListToInitialize.begin() + i); + i--; + continue; + } + + FEEntity* EntityToWorkWith = Scene->GetEntity(EntityID); + if (EntityToWorkWith == nullptr) + { + EnitityIDListToInitialize.erase(EnitityIDListToInitialize.begin() + i); + i--; + continue; + } + + if (!EntityToWorkWith->HasComponent()) + { + EnitityIDListToInitialize.erase(EnitityIDListToInitialize.begin() + i); + i--; + continue; + } + + FEInstancedComponent& InstancedComponent = EntityToWorkWith->GetComponent(); + + if (InstancedComponent.TerrainToSnap == nullptr && !InstancedComponent.PostponedTerrainToSnapID.empty()) + { + FEEntity* TerrainEntity = Scene->GetEntity(InstancedComponent.PostponedTerrainToSnapID); + if (TerrainEntity != nullptr) + { + TERRAIN_SYSTEM.SnapInstancedEntity(TerrainEntity, EntityToWorkWith); + + if (InstancedComponent.PostponedTerrainLayer != -1) + TERRAIN_SYSTEM.ConnectInstancedEntityToLayer(TerrainEntity, EntityToWorkWith, InstancedComponent.PostponedTerrainLayer); + } + } + + PopulateInstance(EntityToWorkWith, InstancedComponent.SpawnInfo); + + if (!InstancedComponent.PostponedModificationsData.isNull()) + { + Json::Value EntityFileRoot = InstancedComponent.PostponedModificationsData; + + size_t Count = EntityFileRoot.size(); + for (int j = 0; j < Count; j++) + { + if (EntityFileRoot[j]["Type"].asInt() == FE_CHANGE_DELETED) + { + INSTANCED_RENDERING_SYSTEM.DeleteIndividualInstance(EntityToWorkWith, EntityFileRoot[j]["Index"].asInt()); + } + else if (EntityFileRoot[j]["Type"].asInt() == FE_CHANGE_MODIFIED) + { + glm::mat4 ModifedMatrix; + for (int k = 0; k < 4; k++) + { + for (int p = 0; p < 4; p++) + { + ModifedMatrix[k][p] = EntityFileRoot[j]["Modification"][k][p].asFloat(); + } + } + + INSTANCED_RENDERING_SYSTEM.ModifyIndividualInstance(EntityToWorkWith, EntityFileRoot[j]["Index"].asInt(), ModifedMatrix); + } + else if (EntityFileRoot[j]["Type"].asInt() == FE_CHANGE_ADDED) + { + glm::mat4 ModifedMatrix; + for (int k = 0; k < 4; k++) + { + for (int p = 0; p < 4; p++) + { + ModifedMatrix[k][p] = EntityFileRoot[j]["Modification"][k][p].asFloat(); + } + } + + INSTANCED_RENDERING_SYSTEM.AddIndividualInstance(EntityToWorkWith, ModifedMatrix); + } + } + } + + EnitityIDListToInitialize.erase(EnitityIDListToInitialize.begin() + i); + i--; + } +} + +void FEInstancedSystem::ClearInstance(FEEntity* Entity) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + InstancedComponent.InstanceCount = 0; + + for (size_t i = 0; i < InstancedComponent.InstancedElementsData.size(); i++) + { + delete[] InstancedComponent.InstancedElementsData[i]->LODCounts; + + InstancedComponent.InstancedElementsData[i]->InstancedAABBSizes.resize(0); + InstancedComponent.InstancedElementsData[i]->InstancedMatrices.resize(0); + InstancedComponent.InstancedElementsData[i]->TransformedInstancedMatrices.resize(0); + InstancedComponent.InstancedElementsData[i]->InstancePositions.resize(0); + } + InstancedComponent.Modifications.clear(); + + for (size_t i = 0; i < InstancedComponent.InstancedElementsData.size(); i++) + { + FEEntity* EntityWithGameModel = GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[i]->EntityIDWithGameModelComponent); + if (EntityWithGameModel == nullptr) + continue; + + FEGameModelComponent& GameModelComponent = EntityWithGameModel->GetComponent(); + InstancedComponent.InstancedElementsData[i]->LODCounts = new int[GameModelComponent.GetGameModel()->GetMaxLODCount()]; + for (size_t j = 0; j < GameModelComponent.GetGameModel()->GetMaxLODCount(); j++) + InstancedComponent.InstancedElementsData[i]->LODCounts[j] = 0; + + InstancedComponent.InstancedElementsData[i]->InstancedMatricesLOD.resize(GameModelComponent.GetGameModel()->GetMaxLODCount()); + } + + Entity->GetComponent().SetScale(glm::vec3(1.0f)); +} + +bool FEInstancedSystem::PopulateInstance(FEEntity* Entity, FESpawnInfo SpawnInfo) +{ + if (Entity == nullptr) + return false; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return false; + + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + FEEntity* GameModelEntity = GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[0]->EntityIDWithGameModelComponent); + if (GameModelEntity == nullptr) + return false; + + FEGameModelComponent& GameModelComponent = GameModelEntity->GetComponent(); + return PopulateInstanceInternal(Entity, GameModelComponent, SpawnInfo); +} + +bool FEInstancedSystem::PopulateInstanceInternal(FEEntity* Entity, FEGameModelComponent& GameModelComponent, FESpawnInfo SpawnInfo) +{ + if (SpawnInfo.Radius <= 0.0f || SpawnInfo.ObjectCount < 1 || SpawnInfo.ObjectCount > 1000000 || GameModelComponent.GetGameModel() == nullptr) + return false; + + FETransformComponent& TransformComponent = Entity->GetComponent(); + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + InstancedComponent.SpawnInfo = SpawnInfo; + srand(SpawnInfo.Seed); + + const glm::vec3 Min = GameModelComponent.GetGameModel()->GetMesh()->GetAABB().GetMin(); + const glm::vec3 Max = GameModelComponent.GetGameModel()->GetMesh()->GetAABB().GetMax(); + + float YSize = sqrt((Max.y - Min.y) * (Max.y - Min.y)); + YSize *= GameModelComponent.GetGameModel()->GetScaleFactor(); + + std::vector NewMats; + NewMats.resize(SpawnInfo.ObjectCount); + + glm::vec3 Position = TransformComponent.GetPosition(); + + for (size_t i = 0; i < NewMats.size(); i++) + { + glm::mat4 NewMat = glm::mat4(1.0); + // spawner transformation would be taken in account later so consider center in 0 + float X = SpawnInfo.GetPositionDeviation(); + float Z = SpawnInfo.GetPositionDeviation(); + float Y = SpawnInfo.GetPositionDeviation(); + + if (InstancedComponent.TerrainToSnap != nullptr) + { + Y = TERRAIN_SYSTEM.GetHeightAt(InstancedComponent.TerrainToSnap, glm::vec2(Position.x + X, Position.z + Z)); + + if (InstancedComponent.TerrainLayer != -1 && Y != -FLT_MAX) + { + FETerrainComponent& TerrainComponent = InstancedComponent.TerrainToSnap->GetComponent(); + const float LayerIntensity = TERRAIN_SYSTEM.GetLayerIntensityAt(InstancedComponent.TerrainToSnap, glm::vec2(Position.x + X, Position.z + Z), InstancedComponent.TerrainLayer); + if (LayerIntensity < InstancedComponent.MinLayerIntensityToSpawn) + Y = -FLT_MAX; + } + + int CountOfTries = 0; + while (Y == -FLT_MAX) + { + X = SpawnInfo.GetPositionDeviation(); + Z = SpawnInfo.GetPositionDeviation(); + Y = TERRAIN_SYSTEM.GetHeightAt(InstancedComponent.TerrainToSnap, glm::vec2(Position.x + X, Position.z + Z)); + + if (InstancedComponent.TerrainLayer != -1 && Y != -FLT_MAX) + { + FETerrainComponent& TerrainComponent = InstancedComponent.TerrainToSnap->GetComponent(); + const float LayerIntensity = TERRAIN_SYSTEM.GetLayerIntensityAt(InstancedComponent.TerrainToSnap, glm::vec2(Position.x + X, Position.z + Z), InstancedComponent.TerrainLayer); + if (LayerIntensity < InstancedComponent.MinLayerIntensityToSpawn) + Y = -FLT_MAX; + } + + CountOfTries++; + if (CountOfTries > 300) + break; + } + + if (CountOfTries > 300) + { + Y = Position.y + SpawnInfo.GetPositionDeviation(); + } + } + + NewMat = glm::translate(NewMat, glm::vec3(X, Y, Z)); + + NewMat = glm::rotate(NewMat, SpawnInfo.GetRotaionDeviation(glm::vec3(1, 0, 0)) * ANGLE_TORADIANS_COF, glm::vec3(1, 0, 0)); + NewMat = glm::rotate(NewMat, SpawnInfo.GetRotaionDeviation(glm::vec3(0, 1, 0)) * ANGLE_TORADIANS_COF, glm::vec3(0, 1, 0)); + NewMat = glm::rotate(NewMat, SpawnInfo.GetRotaionDeviation(glm::vec3(0, 0, 1)) * ANGLE_TORADIANS_COF, glm::vec3(0, 0, 1)); + + float FinalScale = GameModelComponent.GetGameModel()->GetScaleFactor() + GameModelComponent.GetGameModel()->GetScaleFactor() * SpawnInfo.GetScaleDeviation(); + if (FinalScale < 0.0f) + FinalScale = 0.01f; + NewMat = glm::scale(NewMat, glm::vec3(FinalScale)); + + NewMats[i] = NewMat; + } + AddInstances(Entity, NewMats.data(), NewMats.size()); + + if (InstancedComponent.TerrainToSnap != nullptr) + { + // terrain.Y could be not 0.0f but here we should indicate 0.0f as Y. + TransformComponent.SetPosition(glm::vec3(Position.x, 0.0f, Position.z)); + } + + srand(static_cast(time(nullptr))); + + InstancedComponent.bDirtyFlag = true; + return true; +} + +void FEInstancedSystem::CheckDirtyFlag(FEEntity* Entity) +{ + if (Entity == nullptr) + return; + + if (!Entity->HasComponent() && !Entity->HasComponent()) + return; + + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + size_t CurrentBufferIndex = 0; + if (Entity->HasComponent()) + { + FEPrefabInstanceComponent& PrefabInstanceComponent = Entity->GetComponent(); + FEScene* PrefabScene = PrefabInstanceComponent.GetPrefab()->GetScene(); + + std::vector AllPrefabEntities = PrefabScene->GetEntityIDListWithComponent(); + for (size_t i = 0; i < AllPrefabEntities.size(); i++) + { + FEEntity* CurrentPrefabEntity = PrefabScene->GetEntity(AllPrefabEntities[i]); + if (CurrentPrefabEntity != nullptr) + { + FEGameModelComponent& GameModelComponent = CurrentPrefabEntity->GetComponent(); + if (InstancedComponent.InstancedElementsData[CurrentBufferIndex]->LastFrameGameModel != GameModelComponent.GetGameModel() || GameModelComponent.GetGameModel()->IsDirty()) + { + InstancedComponent.bDirtyFlag = true; + InstancedComponent.InstancedElementsData[CurrentBufferIndex]->LastFrameGameModel = GameModelComponent.GetGameModel(); + break; + } + CurrentBufferIndex++; + } + } + } + else + { + FEGameModelComponent& GameModelComponent = Entity->GetComponent(); + if (InstancedComponent.InstancedElementsData[CurrentBufferIndex]->LastFrameGameModel != GameModelComponent.GetGameModel() || GameModelComponent.GetGameModel()->IsDirty()) + { + InstancedComponent.bDirtyFlag = true; + InstancedComponent.InstancedElementsData[CurrentBufferIndex]->LastFrameGameModel = GameModelComponent.GetGameModel(); + } + } + + FETransformComponent& TransformComponent = Entity->GetComponent(); + + if (TransformComponent.IsDirty()) + InstancedComponent.bDirtyFlag = true; + + if (InstancedComponent.bDirtyFlag) + { + UpdateBuffers(Entity); + InstancedComponent.bDirtyFlag = false; + InitializeGPUCullingBuffers(Entity); + // It is not correct to set dirty flags here. + //GameModelComponent.GetGameModel()->SetDirtyFlag(false); + + if (Entity->HasComponent()) + { + FEPrefabInstanceComponent& PrefabInstanceComponent = Entity->GetComponent(); + FEScene* PrefabScene = PrefabInstanceComponent.GetPrefab()->GetScene(); + + std::vector AllPrefabEntities = PrefabScene->GetEntityIDListWithComponent(); + for (size_t i = 0; i < AllPrefabEntities.size(); i++) + { + FEEntity* CurrentPrefabEntity = PrefabScene->GetEntity(AllPrefabEntities[i]); + if (CurrentPrefabEntity != nullptr) + { + FEGameModelComponent& GameModelComponent = CurrentPrefabEntity->GetComponent(); + GameModelComponent.GetGameModel()->SetDirtyFlag(false); + } + } + } + else + { + FEGameModelComponent& GameModelComponent = Entity->GetComponent(); + GameModelComponent.GetGameModel()->SetDirtyFlag(false); + } + } + + if (TransformComponent.IsDirty()) + { + TransformComponent.SetDirtyFlag(false); + UpdateMatrices(Entity); + } +} + +void FEInstancedSystem::UpdateIndividualSelectModeAABBData(FEEntity* EntityWithInstancedComponent) +{ + if (EntityWithInstancedComponent == nullptr) + return; + + if (!EntityWithInstancedComponent->HasComponent()) + return; + + FEGameModelComponent& GameModelComponent = EntityWithInstancedComponent->GetComponent(); + FEInstancedComponent& InstancedComponent = EntityWithInstancedComponent->GetComponent(); + UpdateIndividualSelectModeAABBData(GameModelComponent, InstancedComponent); +} + +void FEInstancedSystem::UpdateIndividualSelectModeAABBData(FEGameModelComponent& GameModelComponent, FEInstancedComponent& InstancedComponent) +{ + InstancedComponent.IndividualInstancedAABB.clear(); + InstancedComponent.IndividualInstancedAABB.resize(InstancedComponent.InstanceCount); + + for (size_t i = 0; i < InstancedComponent.InstanceCount; i++) + { + for (size_t j = 0; j < InstancedComponent.InstancedElementsData.size(); j++) + { + FEEntity* EntityWithGameModel = GetEntityWithGameModelComponent(InstancedComponent.InstancedElementsData[j]->EntityIDWithGameModelComponent); + if (EntityWithGameModel == nullptr) + continue; + + FEGameModelComponent& GameModelComponent = EntityWithGameModel->GetComponent(); + InstancedComponent.IndividualInstancedAABB[i] = InstancedComponent.IndividualInstancedAABB[i].Merge(FEAABB(GameModelComponent.GetGameModel()->GetMesh()->GetAABB(), InstancedComponent.InstancedElementsData[j]->TransformedInstancedMatrices[i])); + } + } + + InstancedComponent.bDirtyFlag = true; +} + +bool FEInstancedSystem::IsIndividualSelectMode(FEEntity* EntityWithInstancedComponent) +{ + if (EntityWithInstancedComponent == nullptr) + return false; + + if (!EntityWithInstancedComponent->HasComponent()) + return false; + + FEInstancedComponent& InstancedComponent = EntityWithInstancedComponent->GetComponent(); + return IsIndividualSelectMode(InstancedComponent); +} + +bool FEInstancedSystem::IsIndividualSelectMode(FEInstancedComponent& InstancedComponent) +{ + return InstancedComponent.bSelectionMode; +} + +void FEInstancedSystem::SetIndividualSelectMode(FEEntity* EntityWithInstancedComponent, const bool NewValue) +{ + if (EntityWithInstancedComponent == nullptr) + return; + + if (!EntityWithInstancedComponent->HasComponent()) + return; + + FEGameModelComponent& GameModelComponent = EntityWithInstancedComponent->GetComponent(); + FEInstancedComponent& InstancedComponent = EntityWithInstancedComponent->GetComponent(); + SetIndividualSelectMode(GameModelComponent, InstancedComponent, NewValue); +} + +void FEInstancedSystem::SetIndividualSelectMode(FEGameModelComponent& GameModelComponent, FEInstancedComponent& InstancedComponent, const bool NewValue) +{ + if (NewValue) + UpdateIndividualSelectModeAABBData(GameModelComponent, InstancedComponent); + + InstancedComponent.bSelectionMode = NewValue; +} + +Json::Value FEInstancedSystem::InstanceComponentToJson(FEEntity* Entity) +{ + Json::Value Root; + if (Entity == nullptr || !Entity->HasComponent()) + { + LOG.Add("FEInstancedSystem::InstanceComponentToJson Entity is nullptr or does not have FEInstancedComponent", "FE_LOG_ECS", FE_LOG_WARNING); + return Root; + } + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + Root["Modifications to spawn"] = InstancedComponent.GetSpawnModificationCount() == 0 ? false : true; + if (InstancedComponent.GetSpawnModificationCount()) + { + Json::Value ModificationsData; + auto ModificationList = InstancedComponent.GetSpawnModifications(); + for (int j = 0; j < ModificationList.size(); j++) + { + ModificationsData[j]["Type"] = static_cast(ModificationList[j].Type); + ModificationsData[j]["Index"] = ModificationList[j].Index; + if (ModificationList[j].Type != FE_CHANGE_DELETED) + { + for (int k = 0; k < 4; k++) + { + for (int p = 0; p < 4; p++) + { + ModificationsData[j]["Modification"][k][p] = ModificationList[j].Modification[k][p]; + } + } + } + } + Root["Modifications"] = ModificationsData; + } + + Root["Spawn info"]["Seed"] = InstancedComponent.SpawnInfo.Seed; + Root["Spawn info"]["Object count"] = InstancedComponent.SpawnInfo.ObjectCount; + Root["Spawn info"]["Radius"] = InstancedComponent.SpawnInfo.Radius; + Root["Spawn info"]["Min scale"] = InstancedComponent.SpawnInfo.GetMinScale(); + Root["Spawn info"]["Max scale"] = InstancedComponent.SpawnInfo.GetMaxScale(); + Root["Spawn info"]["RotationDeviation"]["X"] = InstancedComponent.SpawnInfo.RotationDeviation.x; + Root["Spawn info"]["RotationDeviation"]["Y"] = InstancedComponent.SpawnInfo.RotationDeviation.y; + Root["Spawn info"]["RotationDeviation"]["Z"] = InstancedComponent.SpawnInfo.RotationDeviation.z; + + if (InstancedComponent.GetSnappedToTerrain() == nullptr) + { + Root["Snapped to terrain ID"] = "none"; + } + else + { + Root["Snapped to terrain ID"] = InstancedComponent.GetSnappedToTerrain()->GetObjectID(); + Root["Terrain layer"] = InstancedComponent.GetTerrainLayer(); + Root["Min layer intensity to spawn"] = InstancedComponent.GetMinimalLayerIntensityToSpawn(); + } + + return Root; +} + +void FEInstancedSystem::InstanceComponentFromJson(FEEntity* Entity, Json::Value Data) +{ + if (Entity == nullptr) + { + LOG.Add("FEInstancedSystem::InstanceComponentFromJson Entity is nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + Entity->AddComponent(); + FEInstancedComponent& InstancedComponent = Entity->GetComponent(); + + InstancedComponent.SpawnInfo.Seed = Data["Spawn info"]["Seed"].asInt(); + InstancedComponent.SpawnInfo.ObjectCount = Data["Spawn info"]["Object count"].asInt(); + InstancedComponent.SpawnInfo.Radius = Data["Spawn info"]["Radius"].asFloat(); + InstancedComponent.SpawnInfo.SetMinScale(Data["Spawn info"]["Min scale"].asFloat()); + InstancedComponent.SpawnInfo.SetMaxScale(Data["Spawn info"]["Max scale"].asFloat()); + InstancedComponent.SpawnInfo.RotationDeviation.x = Data["Spawn info"]["RotationDeviation"]["X"].asFloat(); + InstancedComponent.SpawnInfo.RotationDeviation.y = Data["Spawn info"]["RotationDeviation"]["Y"].asFloat(); + InstancedComponent.SpawnInfo.RotationDeviation.z = Data["Spawn info"]["RotationDeviation"]["Z"].asFloat(); + + if (Data["Snapped to terrain ID"].asString() != "none") + { + InstancedComponent.PostponedTerrainToSnapID = Data["Snapped to terrain ID"].asString(); + InstancedComponent.TerrainToSnap = nullptr; + + if (Data.isMember("Terrain layer")) + { + InstancedComponent.PostponedTerrainLayer = Data["Terrain layer"].asInt(); + InstancedComponent.SetMinimalLayerIntensityToSpawn(Data["Min layer intensity to spawn"].asFloat()); + } + } + + // Postponing this operation when loading is finished. + INSTANCED_RENDERING_SYSTEM.EnitityIDListToInitialize.push_back(std::make_pair(Entity->GetParentScene()->GetObjectID(), Entity->GetObjectID())); + + if (Data["Modifications to spawn"].asBool()) + { + InstancedComponent.PostponedModificationsData = Data["Modifications"]; + } +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FEInstancedSystem.h b/SubSystems/Scene/Components/Systems/FEInstancedSystem.h new file mode 100644 index 0000000..1e9ae8f --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FEInstancedSystem.h @@ -0,0 +1,98 @@ +#pragma once +#include "../../Scene/FESceneManager.h" + +namespace FocalEngine +{ + class FOCAL_ENGINE_API FEInstancedSystem + { + friend class FEScene; + friend class FERenderer; + friend class FEngine; + + SINGLETON_PRIVATE_PART(FEInstancedSystem) + + bool bInternalAdd = false; + + std::vector> EnitityIDListToInitialize; + + static void OnMyComponentAdded(FEEntity* Entity); + static void OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing); + void RegisterOnComponentCallbacks(); + + void InitializeBuffers(FEEntity* Entity); + void InitializeBuffer(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex = 0); + void InitializeGPUCullingBuffers(FEEntity* Entity); + void InitializeGPUCullingBuffer(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex = 0); + + static void DuplicateInstancedComponent(FEEntity* SourceEntity, FEEntity* TargetEntity); + + bool PopulateInstanceInternal(FEEntity* Entity, FEGameModelComponent& GameModelComponent, FESpawnInfo SpawnInfo); + + void AddInstanceInternal(FEEntity* Entity, glm::mat4 InstanceMatrix); + void AddInstanceInternal(FEEntity* Entity, FEGameModelComponent& GameModelComponent, glm::mat4 InstanceMatrix, size_t BufferIndex = 0); + void AddInstances(FEEntity* EntityWithInstancedComponent, const glm::mat4* InstanceMatrix, size_t Count); + void AddInstances(FEEntity* Entity, FEGameModelComponent& GameModelComponent, const glm::mat4* InstanceMatrix, size_t Count, size_t BufferIndex = 0); + + void UpdateBuffers(FEEntity* Entity); + void UpdateBuffer(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex = 0); + + void UpdateMatrices(FEEntity* Entity); + void UpdateMatrix(FEEntity* Entity, size_t BufferIndex = 0); + + void Update(); + + void Render(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex = 0); + void RenderGameModelComponent(FEGameModelComponent& GameModelComponent, FEInstancedComponent& InstancedComponent, size_t BufferIndex = 0); + + void RenderOnlyBillbords(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex); + void RenderOnlyBillbordsInternal(FEEntity* Entity, FEGameModelComponent& GameModelComponent, size_t BufferIndex = 0); + + void CheckDirtyFlag(FEEntity* Entity); + + static Json::Value InstanceComponentToJson(FEEntity* Entity); + static void InstanceComponentFromJson(FEEntity* Entity, Json::Value Root); + public: + SINGLETON_PUBLIC_PART(FEInstancedSystem) + + void AddIndividualInstance(FEEntity* Entity, glm::mat4 InstanceMatrix); + void DeleteIndividualInstance(FEEntity* Entity, const size_t InstanceIndex); + void ModifyIndividualInstance(FEEntity* Entity, const size_t InstanceIndex, glm::mat4 NewMatrix); + + bool TryToSnapIndividualInstance(FEEntity* Entity, size_t InstanceIndex); + + void ClearInstance(FEEntity* Entity); + + FEAABB GetAABB(FEEntity* Entity); + + bool PopulateInstance(FEEntity* Entity, FESpawnInfo SpawnInfo); + + void UpdateIndividualSelectModeAABBData(FEEntity* EntityWithInstancedComponent); + void UpdateIndividualSelectModeAABBData(FEGameModelComponent& GameModelComponent, FEInstancedComponent& InstancedComponent); + + bool IsIndividualSelectMode(FEEntity* EntityWithInstancedComponent); + bool IsIndividualSelectMode(FEInstancedComponent& InstancedComponent); + + void SetIndividualSelectMode(FEEntity* EntityWithInstancedComponent, const bool NewValue); + void SetIndividualSelectMode(FEGameModelComponent& GameModelComponent, FEInstancedComponent& InstancedComponent, const bool NewValue); + + FEEntity* GetEntityWithGameModelComponent(std::string EntityID) + { + FEObject* Object = OBJECT_MANAGER.GetFEObject(EntityID); + if (Object == nullptr || Object->GetType() != FE_ENTITY) + return nullptr; + + FEEntity* Entity = reinterpret_cast(Object); + if (Entity == nullptr || !Entity->HasComponent()) + return nullptr; + + return Entity; + } + }; + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetInstancedSystem(); + #define INSTANCED_RENDERING_SYSTEM (*static_cast(GetInstancedSystem())) +#else + #define INSTANCED_RENDERING_SYSTEM FEInstancedSystem::GetInstance() +#endif +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FELightSystem.cpp b/SubSystems/Scene/Components/Systems/FELightSystem.cpp new file mode 100644 index 0000000..3066932 --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FELightSystem.cpp @@ -0,0 +1,583 @@ +#include "FELightSystem.h" +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetLightSystem() +{ + return FELightSystem::GetInstancePointer(); +} +#endif + +FELightSystem::FELightSystem() +{ + RegisterOnComponentCallbacks(); + COMPONENTS_TOOL.RegisterComponentToJsonFunction(LightComponentToJson); + COMPONENTS_TOOL.RegisterComponentFromJsonFunction(LightComponentFromJson); + COMPONENTS_TOOL.RegisterComponentDuplicateFunction(DuplicateLightComponent); +} + +void FELightSystem::RegisterOnComponentCallbacks() +{ + SCENE_MANAGER.RegisterOnComponentConstructCallback(OnMyComponentAdded); + SCENE_MANAGER.RegisterOnComponentDestroyCallback(OnMyComponentDestroy); +} + +void FELightSystem::OnMyComponentAdded(FEEntity* Entity) +{ + if (LIGHT_SYSTEM.bInternalAdd) + return; + + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FELightComponent& LightComponent = Entity->GetComponent(); + if (LightComponent.GetType() == FE_DIRECTIONAL_LIGHT) + { + LIGHT_SYSTEM.DirectionalLightInitialization(LightComponent); + } +} + +void FELightSystem::DirectionalLightInitialization(FELightComponent& LightComponent) +{ + if (LightComponent.GetType() != FE_DIRECTIONAL_LIGHT) + return; + + LightComponent.CascadeData[0].FrameBuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_DEPTH_ATTACHMENT, 1024 * 2, 1024 * 2); + LightComponent.CascadeData[1].FrameBuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_DEPTH_ATTACHMENT, 1024 * 2, 1024 * 2); + LightComponent.CascadeData[2].FrameBuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_DEPTH_ATTACHMENT, 1024 * 2, 1024 * 2); + LightComponent.CascadeData[3].FrameBuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_DEPTH_ATTACHMENT, 1024 * 2, 1024 * 2); + + // To clear cascades framebuffers. + LightComponent.SetCastShadows(false); + LightComponent.SetCastShadows(true); +} + +void FELightSystem::DuplicateLightComponent(FEEntity* SourceEntity, FEEntity* TargetEntity) +{ + if (SourceEntity == nullptr || TargetEntity == nullptr || !SourceEntity->HasComponent()) + return; + + FELightComponent& LightComponent = SourceEntity->GetComponent(); + + LIGHT_SYSTEM.bInternalAdd = true; + TargetEntity->AddComponent(LightComponent.GetType()); + LIGHT_SYSTEM.bInternalAdd = false; + FELightComponent& NewLightComponent = TargetEntity->GetComponent(); + + NewLightComponent = LightComponent; + + if (LightComponent.GetType() == FE_DIRECTIONAL_LIGHT) + LIGHT_SYSTEM.DirectionalLightInitialization(NewLightComponent); +} + +void FELightSystem::OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FELightComponent& LightComponent = Entity->GetComponent(); + if (LightComponent.GetType() == FE_DIRECTIONAL_LIGHT) + { + delete LightComponent.CascadeData[0].FrameBuffer; + delete LightComponent.CascadeData[1].FrameBuffer; + delete LightComponent.CascadeData[2].FrameBuffer; + delete LightComponent.CascadeData[3].FrameBuffer; + } +} + +FELightSystem::~FELightSystem() {}; + +glm::vec3 FELightSystem::GetDirection(FEEntity* LightEntity) +{ + if (LightEntity == nullptr || !LightEntity->HasComponent()) + { + LOG.Add("FELightSystem::GetDirection LightEntity is nullptr or does not have FELightComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return glm::vec3(0.0f); + } + FELightComponent& LightComponent = LightEntity->GetComponent(); + + FETransformComponent& Transform = LightEntity->GetComponent(); + LightComponent.Direction = glm::normalize(Transform.GetWorldMatrix() * glm::vec4(LightComponent.DefaultDirection, 0.0f)); + + return LightComponent.Direction; +} + +// TODO: This should use the main camera internally instead of requiring all parameters. +void FELightSystem::UpdateCascades(FEEntity* LightEntity, float CameraFov, float AspectRatio, float NearPlane, float FarPlane, glm::mat4 ViewMatrix, glm::vec3 CameraForward, glm::vec3 CameraRight, glm::vec3 CameraUp) +{ + if (LightEntity == nullptr || !LightEntity->HasComponent()) + { + LOG.Add("FELightSystem::GetDirection LightEntity is nullptr or does not have FELightComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + FELightComponent& LightComponent = LightEntity->GetComponent(); + + if (LightComponent.GetType() != FE_DIRECTIONAL_LIGHT) + { + LOG.Add("FELightSystem::UpdateCascades LightComponent is not a directional light", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + + FETransformComponent& TransformComponent = LightEntity->GetComponent(); + + static glm::vec4 BasisX = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f); + static glm::vec4 BasisY = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f); + static glm::vec4 BasisZ = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f); + + glm::vec4 FbasisX = glm::normalize(glm::toMat4(TransformComponent.GetQuaternion()) * BasisX); + glm::vec4 FbasisY = glm::normalize(glm::toMat4(TransformComponent.GetQuaternion()) * BasisY); + glm::vec4 FbasisZ = glm::normalize(glm::toMat4(TransformComponent.GetQuaternion()) * BasisZ); + + glm::mat4 CascadeView = glm::mat4(1.0f); + CascadeView[0][0] = FbasisX.x; + CascadeView[1][0] = FbasisX.y; + CascadeView[2][0] = FbasisX.z; + CascadeView[0][1] = FbasisY.x; + CascadeView[1][1] = FbasisY.y; + CascadeView[2][1] = FbasisY.z; + CascadeView[0][2] = FbasisZ.x; + CascadeView[1][2] = FbasisZ.y; + CascadeView[2][2] = FbasisZ.z; + + FarPlane = NearPlane; + glm::mat4 InverseVm = glm::inverse(ViewMatrix); + static std::vector FrustumEdges; + FrustumEdges.resize(8); + + float TestCascadeDistanceScaleFactor = 4.0f; + float TestCSM0 = 0.050f; + float TestCSM1 = 0.150f; + float TestCSM2 = 0.550f; + float TestCSM3 = 1.1f; + float TestCSMScale = 1.75f; + + float TestCSMType = 1; + + // old and incorrect + if (TestCSMType == 0) + { + for (size_t i = 0; i < 4; i++) + { + LightComponent.CascadeData[i].ViewMat = CascadeView; + + NearPlane = FarPlane; + FarPlane = LightComponent.ShadowCoverage * (0.0447f * static_cast(pow(2.1867f, (i + 1)))); + LightComponent.CascadeData[i].Size = static_cast(static_cast(FarPlane) - 1) * TestCascadeDistanceScaleFactor; + if (LightComponent.CascadeData[i].Size <= 0.01f) + LightComponent.CascadeData[i].Size = 1.0; + + float FirstCascadeY1 = NearPlane * tan(glm::radians(CameraFov / 2.0f)); + float FirstCascadeY2 = FarPlane * tan(glm::radians(CameraFov / 2.0f)); + + float FirstCascadeX1 = NearPlane * tan((AspectRatio) / 2.0f); + float FirstCascadeX2 = FarPlane * tan((AspectRatio) / 2.0f); + + FrustumEdges[0] = glm::vec4(FirstCascadeX1, -FirstCascadeY1, -NearPlane, 1.0f); + FrustumEdges[1] = glm::vec4(FirstCascadeX1, FirstCascadeY1, -NearPlane, 1.0f); + FrustumEdges[2] = glm::vec4(-FirstCascadeX1, FirstCascadeY1, -NearPlane, 1.0f); + FrustumEdges[3] = glm::vec4(-FirstCascadeX1, -FirstCascadeY1, -NearPlane, 1.0f); + + FrustumEdges[4] = glm::vec4(FirstCascadeX2, -FirstCascadeY2, -FarPlane, 1.0f); + FrustumEdges[5] = glm::vec4(FirstCascadeX2, FirstCascadeY2, -FarPlane, 1.0f); + FrustumEdges[6] = glm::vec4(-FirstCascadeX2, FirstCascadeY2, -FarPlane, 1.0f); + FrustumEdges[7] = glm::vec4(-FirstCascadeX2, -FirstCascadeY2, -FarPlane, 1.0f); + + for (size_t j = 0; j < FrustumEdges.size(); j++) + FrustumEdges[j] = LightComponent.CascadeData[0].ViewMat * InverseVm * FrustumEdges[j]; + + for (size_t j = 0; j < FrustumEdges.size(); j++) + FrustumEdges[j].z = -FrustumEdges[j].z; + + float MinX = FLT_MAX; + float MaxX = FLT_MIN; + float MinY = FLT_MAX; + float MaxY = FLT_MIN; + float MinZ = FLT_MAX; + float MaxZ = FLT_MIN; + + for (size_t j = 0; j < FrustumEdges.size(); j++) + { + MinX = std::min(MinX, FrustumEdges[j].x); + MinY = std::min(MinY, FrustumEdges[j].y); + MinZ = std::min(MinZ, FrustumEdges[j].z); + + MaxX = std::max(MaxX, FrustumEdges[j].x); + MaxY = std::max(MaxY, FrustumEdges[j].y); + MaxZ = std::max(MaxZ, FrustumEdges[j].z); + } + + LightComponent.CascadeData[i].ProjectionMat = glm::ortho(MinX - FarPlane * (LightComponent.CSMXYDepth / 4.0f), MaxX + FarPlane * (LightComponent.CSMXYDepth / 4.0f), + MinY - FarPlane * (LightComponent.CSMXYDepth / 4.0f), MaxY + FarPlane * (LightComponent.CSMXYDepth / 4.0f), + MinZ - FarPlane * LightComponent.CSMZDepth, MaxZ + FarPlane * LightComponent.CSMZDepth); + } + } + else if (TestCSMType == 1) + { + for (size_t i = 0; i < 4; i++) + { + LightComponent.CascadeData[i].ViewMat = CascadeView; + + NearPlane = FarPlane; + if (i == 0) + { + FarPlane = (LightComponent.ShadowCoverage / 4.0f) * TestCSM0; + } + else if (i == 1) + { + FarPlane = (LightComponent.ShadowCoverage / 4.0f) * TestCSM1; + } + else if (i == 2) + { + FarPlane = (LightComponent.ShadowCoverage / 4.0f) * TestCSM2; + } + else if (i == 3) + { + FarPlane = (LightComponent.ShadowCoverage / 4.0f) * TestCSM3; + } + LightComponent.CascadeData[i].Size = FarPlane * TestCSMScale; + + float FirstCascadeY1 = NearPlane * tan(glm::radians(CameraFov / 2.0f)); + float FirstCascadeY2 = FarPlane * tan(glm::radians(CameraFov / 2.0f)); + + float FirstCascadeX1 = NearPlane * tan((AspectRatio) / 2.0f); + float FirstCascadeX2 = FarPlane * tan((AspectRatio) / 2.0f); + + FrustumEdges[0] = glm::vec4(FirstCascadeX1, -FirstCascadeY1, -NearPlane, 1.0f); + FrustumEdges[1] = glm::vec4(FirstCascadeX1, FirstCascadeY1, -NearPlane, 1.0f); + FrustumEdges[2] = glm::vec4(-FirstCascadeX1, FirstCascadeY1, -NearPlane, 1.0f); + FrustumEdges[3] = glm::vec4(-FirstCascadeX1, -FirstCascadeY1, -NearPlane, 1.0f); + + FrustumEdges[4] = glm::vec4(FirstCascadeX2, -FirstCascadeY2, -FarPlane, 1.0f); + FrustumEdges[5] = glm::vec4(FirstCascadeX2, FirstCascadeY2, -FarPlane, 1.0f); + FrustumEdges[6] = glm::vec4(-FirstCascadeX2, FirstCascadeY2, -FarPlane, 1.0f); + FrustumEdges[7] = glm::vec4(-FirstCascadeX2, -FirstCascadeY2, -FarPlane, 1.0f); + + for (size_t j = 0; j < FrustumEdges.size(); j++) + FrustumEdges[j] = LightComponent.CascadeData[0].ViewMat * InverseVm * FrustumEdges[j]; + + for (size_t j = 0; j < FrustumEdges.size(); j++) + FrustumEdges[j].z = -FrustumEdges[j].z; + + float MinX = FLT_MAX; + float MaxX = -FLT_MAX; + float MinY = FLT_MAX; + float MaxY = -FLT_MAX; + float MinZ = FLT_MAX; + float MaxZ = -FLT_MAX; + + for (size_t j = 0; j < FrustumEdges.size(); j++) + { + MinX = std::min(MinX, FrustumEdges[j].x); + MinY = std::min(MinY, FrustumEdges[j].y); + MinZ = std::min(MinZ, FrustumEdges[j].z); + + MaxX = std::max(MaxX, FrustumEdges[j].x); + MaxY = std::max(MaxY, FrustumEdges[j].y); + MaxZ = std::max(MaxZ, FrustumEdges[j].z); + } + + float Left = MinX - FarPlane * (LightComponent.CSMXYDepth / 4.0f); + float Right = MaxX + FarPlane * (LightComponent.CSMXYDepth / 4.0f); + + float Bottom = MinY - FarPlane * (LightComponent.CSMXYDepth / 4.0f); + float Top = MaxY + FarPlane * (LightComponent.CSMXYDepth / 4.0f); + + float ZNear = MinZ - FarPlane * LightComponent.CSMZDepth; + float ZFar = MaxZ + FarPlane * LightComponent.CSMZDepth; + + LightComponent.CascadeData[i].ProjectionMat = glm::ortho(Left, Right, + Bottom, Top, + ZNear, ZFar); + + float Clip[16]; + float T; + + glm::mat4 cliping = LightComponent.CascadeData[i].ProjectionMat * CascadeView; + for (int j = 0; j < 4; j++) + { + Clip[j * 4] = cliping[j][0]; + Clip[j * 4 + 1] = cliping[j][1]; + Clip[j * 4 + 2] = cliping[j][2]; + Clip[j * 4 + 3] = cliping[j][3]; + } + + /* Extract the numbers for the RIGHT plane */ + LightComponent.CascadeData[i].Frustum[0][0] = Clip[3] - Clip[0]; + LightComponent.CascadeData[i].Frustum[0][1] = Clip[7] - Clip[4]; + LightComponent.CascadeData[i].Frustum[0][2] = Clip[11] - Clip[8]; + LightComponent.CascadeData[i].Frustum[0][3] = Clip[15] - Clip[12]; + + /* Normalize the result */ + T = sqrt(LightComponent.CascadeData[i].Frustum[0][0] * LightComponent.CascadeData[i].Frustum[0][0] + LightComponent.CascadeData[i].Frustum[0][1] * LightComponent.CascadeData[i].Frustum[0][1] + LightComponent.CascadeData[i].Frustum[0][2] * LightComponent.CascadeData[i].Frustum[0][2]); + LightComponent.CascadeData[i].Frustum[0][0] /= T; + LightComponent.CascadeData[i].Frustum[0][1] /= T; + LightComponent.CascadeData[i].Frustum[0][2] /= T; + LightComponent.CascadeData[i].Frustum[0][3] /= T; + + /* Extract the numbers for the LEFT plane */ + LightComponent.CascadeData[i].Frustum[1][0] = Clip[3] + Clip[0]; + LightComponent.CascadeData[i].Frustum[1][1] = Clip[7] + Clip[4]; + LightComponent.CascadeData[i].Frustum[1][2] = Clip[11] + Clip[8]; + LightComponent.CascadeData[i].Frustum[1][3] = Clip[15] + Clip[12]; + + /* Normalize the result */ + T = sqrt(LightComponent.CascadeData[i].Frustum[1][0] * LightComponent.CascadeData[i].Frustum[1][0] + LightComponent.CascadeData[i].Frustum[1][1] * LightComponent.CascadeData[i].Frustum[1][1] + LightComponent.CascadeData[i].Frustum[1][2] * LightComponent.CascadeData[i].Frustum[1][2]); + LightComponent.CascadeData[i].Frustum[1][0] /= T; + LightComponent.CascadeData[i].Frustum[1][1] /= T; + LightComponent.CascadeData[i].Frustum[1][2] /= T; + LightComponent.CascadeData[i].Frustum[1][3] /= T; + + /* Extract the BOTTOM plane */ + LightComponent.CascadeData[i].Frustum[2][0] = Clip[3] + Clip[1]; + LightComponent.CascadeData[i].Frustum[2][1] = Clip[7] + Clip[5]; + LightComponent.CascadeData[i].Frustum[2][2] = Clip[11] + Clip[9]; + LightComponent.CascadeData[i].Frustum[2][3] = Clip[15] + Clip[13]; + + /* Normalize the result */ + T = sqrt(LightComponent.CascadeData[i].Frustum[2][0] * LightComponent.CascadeData[i].Frustum[2][0] + LightComponent.CascadeData[i].Frustum[2][1] * LightComponent.CascadeData[i].Frustum[2][1] + LightComponent.CascadeData[i].Frustum[2][2] * LightComponent.CascadeData[i].Frustum[2][2]); + LightComponent.CascadeData[i].Frustum[2][0] /= T; + LightComponent.CascadeData[i].Frustum[2][1] /= T; + LightComponent.CascadeData[i].Frustum[2][2] /= T; + LightComponent.CascadeData[i].Frustum[2][3] /= T; + + /* Extract the TOP plane */ + LightComponent.CascadeData[i].Frustum[3][0] = Clip[3] - Clip[1]; + LightComponent.CascadeData[i].Frustum[3][1] = Clip[7] - Clip[5]; + LightComponent.CascadeData[i].Frustum[3][2] = Clip[11] - Clip[9]; + LightComponent.CascadeData[i].Frustum[3][3] = Clip[15] - Clip[13]; + + /* Normalize the result */ + T = sqrt(LightComponent.CascadeData[i].Frustum[3][0] * LightComponent.CascadeData[i].Frustum[3][0] + LightComponent.CascadeData[i].Frustum[3][1] * LightComponent.CascadeData[i].Frustum[3][1] + LightComponent.CascadeData[i].Frustum[3][2] * LightComponent.CascadeData[i].Frustum[3][2]); + LightComponent.CascadeData[i].Frustum[3][0] /= T; + LightComponent.CascadeData[i].Frustum[3][1] /= T; + LightComponent.CascadeData[i].Frustum[3][2] /= T; + LightComponent.CascadeData[i].Frustum[3][3] /= T; + + /* Extract the FAR plane */ + LightComponent.CascadeData[i].Frustum[4][0] = Clip[3] - Clip[2]; + LightComponent.CascadeData[i].Frustum[4][1] = Clip[7] - Clip[6]; + LightComponent.CascadeData[i].Frustum[4][2] = Clip[11] - Clip[10]; + LightComponent.CascadeData[i].Frustum[4][3] = Clip[15] - Clip[14]; + + /* Normalize the result */ + T = sqrt(LightComponent.CascadeData[i].Frustum[4][0] * LightComponent.CascadeData[i].Frustum[4][0] + LightComponent.CascadeData[i].Frustum[4][1] * LightComponent.CascadeData[i].Frustum[4][1] + LightComponent.CascadeData[i].Frustum[4][2] * LightComponent.CascadeData[i].Frustum[4][2]); + LightComponent.CascadeData[i].Frustum[4][0] /= T; + LightComponent.CascadeData[i].Frustum[4][1] /= T; + LightComponent.CascadeData[i].Frustum[4][2] /= T; + LightComponent.CascadeData[i].Frustum[4][3] /= T; + + /* Extract the NEAR plane */ + LightComponent.CascadeData[i].Frustum[5][0] = Clip[3] + Clip[2]; + LightComponent.CascadeData[i].Frustum[5][1] = Clip[7] + Clip[6]; + LightComponent.CascadeData[i].Frustum[5][2] = Clip[11] + Clip[10]; + LightComponent.CascadeData[i].Frustum[5][3] = Clip[15] + Clip[14]; + + /* Normalize the result */ + T = sqrt(LightComponent.CascadeData[i].Frustum[5][0] * LightComponent.CascadeData[i].Frustum[5][0] + LightComponent.CascadeData[i].Frustum[5][1] * LightComponent.CascadeData[i].Frustum[5][1] + LightComponent.CascadeData[i].Frustum[5][2] * LightComponent.CascadeData[i].Frustum[5][2]); + LightComponent.CascadeData[i].Frustum[5][0] /= T; + LightComponent.CascadeData[i].Frustum[5][1] /= T; + LightComponent.CascadeData[i].Frustum[5][2] /= T; + LightComponent.CascadeData[i].Frustum[5][3] /= T; + } + } + // my way of frustum reconstruction + else if (TestCSMType == 2) + { + float NearPlaneXLength = NearPlane * glm::tan(AspectRatio / 2.0f) * 2.0f; + float NearPlaneYLength = NearPlaneXLength / AspectRatio; + + glm::vec3 CameraPos = glm::vec3(ViewMatrix[3][0], ViewMatrix[3][1], ViewMatrix[3][2]); + + glm::vec3 NearTopLeft = CameraPos; + NearTopLeft += -CameraRight * (NearPlaneXLength / 2.0f); + NearTopLeft += CameraUp * (NearPlaneYLength / 2.0f); + NearTopLeft += CameraForward * NearPlane; + + glm::vec3 NearTopRight = CameraPos; + NearTopRight += CameraRight * (NearPlaneXLength / 2.0f); + NearTopRight += CameraUp * (NearPlaneYLength / 2.0f); + NearTopRight += CameraForward * NearPlane; + + glm::vec3 NearBottomLeft = CameraPos; + NearBottomLeft += -CameraRight * (NearPlaneXLength / 2.0f); + NearBottomLeft += -CameraUp * (NearPlaneYLength / 2.0f); + NearBottomLeft += CameraForward * NearPlane; + + glm::vec3 NearBottomRight = CameraPos; + NearBottomRight += CameraRight * (NearPlaneXLength / 2.0f); + NearBottomRight += -CameraUp * (NearPlaneYLength / 2.0f); + NearBottomRight += CameraForward * NearPlane; + + glm::vec3 CameraToTopLeft = glm::normalize(NearTopLeft - CameraPos); + glm::vec3 CameraToTopRight = glm::normalize(NearTopRight - CameraPos); + glm::vec3 CameraToBottomLeft = glm::normalize(NearBottomLeft - CameraPos); + glm::vec3 CameraToBottomRight = glm::normalize(NearBottomRight - CameraPos); + + for (size_t i = 0; i < 4; i++) + { + LightComponent.CascadeData[i].ViewMat = CascadeView; + + NearPlane = FarPlane; + if (i == 0) + { + FarPlane = (LightComponent.ShadowCoverage / 4.0f) * TestCSM0; + } + else if (i == 1) + { + FarPlane = (LightComponent.ShadowCoverage / 4.0f) * TestCSM1; + } + else if (i == 2) + { + FarPlane = (LightComponent.ShadowCoverage / 4.0f) * TestCSM2; + } + else if (i == 3) + { + FarPlane = (LightComponent.ShadowCoverage / 4.0f) * TestCSM3; + } + LightComponent.CascadeData[i].Size = FarPlane * TestCSMScale; + + CameraPos = glm::vec3(0.0f); + + FrustumEdges[0] = glm::vec4(CameraPos + CameraToTopLeft * NearPlane, 1.0f); + FrustumEdges[1] = glm::vec4(CameraPos + CameraToTopRight * NearPlane, 1.0f); + FrustumEdges[2] = glm::vec4(CameraPos + CameraToBottomLeft * NearPlane, 1.0f); + FrustumEdges[3] = glm::vec4(CameraPos + CameraToBottomRight * NearPlane, 1.0f); + + FrustumEdges[4] = glm::vec4(CameraPos + CameraToTopLeft * FarPlane, 1.0f); + FrustumEdges[5] = glm::vec4(CameraPos + CameraToTopRight * FarPlane, 1.0f); + FrustumEdges[6] = glm::vec4(CameraPos + CameraToBottomLeft * FarPlane, 1.0f); + FrustumEdges[7] = glm::vec4(CameraPos + CameraToBottomRight * FarPlane, 1.0f); + + for (size_t j = 0; j < FrustumEdges.size(); j++) + FrustumEdges[j] = LightComponent.CascadeData[0].ViewMat * InverseVm * FrustumEdges[j]; + + for (size_t j = 0; j < FrustumEdges.size(); j++) + FrustumEdges[j].z = -FrustumEdges[j].z; + + float MinX = FLT_MAX; + float MaxX = -FLT_MAX; + float MinY = FLT_MAX; + float MaxY = -FLT_MAX; + float MinZ = FLT_MAX; + float MaxZ = -FLT_MAX; + + for (size_t j = 0; j < FrustumEdges.size(); j++) + { + MinX = std::min(MinX, FrustumEdges[j].x); + MinY = std::min(MinY, FrustumEdges[j].y); + MinZ = std::min(MinZ, FrustumEdges[j].z); + + MaxX = std::max(MaxX, FrustumEdges[j].x); + MaxY = std::max(MaxY, FrustumEdges[j].y); + MaxZ = std::max(MaxZ, FrustumEdges[j].z); + } + + LightComponent.CascadeData[i].ProjectionMat = glm::ortho(MinX - FarPlane * (LightComponent.CSMXYDepth / 4.0f), MaxX + FarPlane * (LightComponent.CSMXYDepth / 4.0f), + MinY - FarPlane * (LightComponent.CSMXYDepth / 4.0f), MaxY + FarPlane * (LightComponent.CSMXYDepth / 4.0f), + MinZ - FarPlane * LightComponent.CSMZDepth, MaxZ + FarPlane * LightComponent.CSMZDepth); + } + } +} + +Json::Value FELightSystem::LightComponentToJson(FEEntity* Entity) +{ + Json::Value Root; + if (Entity == nullptr || !Entity->HasComponent()) + { + LOG.Add("FELightSystem::LightComponentToJson Entity is nullptr or does not have FELightComponent", "FE_LOG_ECS", FE_LOG_WARNING); + return Root; + } + FELightComponent& LightComponent = Entity->GetComponent(); + + Root["Type"] = LightComponent.GetType(); + + Root["Color"] = Json::Value(); + Root["Color"]["R"] = LightComponent.Color.x; + Root["Color"]["G"] = LightComponent.Color.y; + Root["Color"]["B"] = LightComponent.Color.z; + Root["Intensity"] = LightComponent.Intensity; + Root["IsEnabled"] = LightComponent.bEnabled; + + Root["CastShadows"] = LightComponent.bCastShadows; + Root["IsUsingCascadeShadows"] = LightComponent.bUseCascadeShadows; + Root["ActiveCascades"] = LightComponent.ActiveCascades; + Root["ShadowCoverage"] = LightComponent.ShadowCoverage; + Root["CSMXYDepth"] = LightComponent.CSMXYDepth; + Root["CSMZDepth"] = LightComponent.CSMZDepth; + + Root["Static ShadowBias"] = LightComponent.IsStaticShadowBias(); + Root["ShadowBias"] = LightComponent.GetShadowBias(); + Root["ShadowBias VariableIntensity"] = LightComponent.GetShadowBiasVariableIntensity(); + Root["Shadow BlurFactor"] = LightComponent.GetShadowBlurFactor(); + + Root["SpotAngle"] = LightComponent.GetSpotAngle(); + Root["SpotAngle Outer"] = LightComponent.GetSpotAngleOuter(); + Root["Direction"]["X"] = LightComponent.Direction.x; + Root["Direction"]["Y"] = LightComponent.Direction.y; + Root["Direction"]["Z"] = LightComponent.Direction.z; + + Root["Range"] = LightComponent.GetRange(); + + return Root; +} + +void FELightSystem::LightComponentFromJson(FEEntity* Entity, Json::Value Root) +{ + if (Entity == nullptr) + { + LOG.Add("FELightSystem::LightComponentFromJson Entity is nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + + int LightType = Root["Type"].asInt(); + FE_LIGHT_TYPE FELightType; + switch (LightType) + { + case 0: + FELightType = FE_NULL_LIGHT; + break; + + case 1: + FELightType = FE_DIRECTIONAL_LIGHT; + break; + + case 2: + FELightType = FE_POINT_LIGHT; + break; + + case 3: + FELightType = FE_SPOT_LIGHT; + break; + + default: + FELightType = FE_NULL_LIGHT; + break; + } + + Entity->AddComponent(FELightType); + FELightComponent& LightComponent = Entity->GetComponent(); + + LightComponent.SetColor(glm::vec3(Root["Color"]["R"].asFloat(), Root["Color"]["G"].asFloat(), Root["Color"]["B"].asFloat())); + LightComponent.SetIntensity(Root["Intensity"].asFloat()); + LightComponent.SetLightEnabled(Root["IsEnabled"].asBool()); + + LightComponent.SetCastShadows(Root["CastShadows"].asBool()); + LightComponent.bUseCascadeShadows = Root["IsUsingCascadeShadows"].asBool(); + LightComponent.SetActiveCascades(Root["ActiveCascades"].asInt()); + LightComponent.SetShadowCoverage(Root["ShadowCoverage"].asFloat()); + LightComponent.SetCSMXYDepth(Root["CSMXYDepth"].asFloat()); + LightComponent.SetCSMZDepth(Root["CSMZDepth"].asFloat()); + + LightComponent.SetIsStaticShadowBias(Root["Static ShadowBias"].asBool()); + LightComponent.SetShadowBias(Root["ShadowBias"].asFloat()); + LightComponent.SetShadowBiasVariableIntensity(Root["ShadowBias VariableIntensity"].asBool()); + LightComponent.SetShadowBlurFactor(Root["Shadow BlurFactor"].asFloat()); + + LightComponent.SetSpotAngle(Root["SpotAngle"].asFloat()); + LightComponent.SetSpotAngleOuter(Root["SpotAngle Outer"].asFloat()); + LightComponent.Direction.x = Root["Direction"]["X"].asFloat(); + LightComponent.Direction.y = Root["Direction"]["Y"].asFloat(); + LightComponent.Direction.z = Root["Direction"]["Z"].asFloat(); + + LightComponent.SetRange(Root["Range"].asFloat()); +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FELightSystem.h b/SubSystems/Scene/Components/Systems/FELightSystem.h new file mode 100644 index 0000000..d391732 --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FELightSystem.h @@ -0,0 +1,39 @@ +#pragma once +#include "../../Scene/FESceneManager.h" + +namespace FocalEngine +{ + class FOCAL_ENGINE_API FELightSystem + { + friend class FEScene; + friend class FERenderer; + friend class FEngine; + + SINGLETON_PRIVATE_PART(FELightSystem) + + bool bInternalAdd = false; + + static void OnMyComponentAdded(FEEntity* Entity); + static void OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing); + void RegisterOnComponentCallbacks(); + + void DirectionalLightInitialization(FELightComponent& LightComponent); + static void DuplicateLightComponent(FEEntity* SourceEntity, FEEntity* TargetEntity); + + static Json::Value LightComponentToJson(FEEntity* Entity); + static void LightComponentFromJson(FEEntity* Entity, Json::Value Root); + public: + SINGLETON_PUBLIC_PART(FELightSystem) + + glm::vec3 GetDirection(FEEntity* LightEntity); + + void UpdateCascades(FEEntity* LightEntity, float CameraFov, float AspectRatio, float NearPlane, float FarPlane, glm::mat4 ViewMatrix, glm::vec3 CameraForward, glm::vec3 CameraRight, glm::vec3 CameraUp); + }; + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetLightSystem(); + #define LIGHT_SYSTEM (*static_cast(GetLightSystem())) +#else + #define LIGHT_SYSTEM FELightSystem::GetInstance() +#endif +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FEPrefabInstanceSystem.cpp b/SubSystems/Scene/Components/Systems/FEPrefabInstanceSystem.cpp new file mode 100644 index 0000000..37e4dc9 --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FEPrefabInstanceSystem.cpp @@ -0,0 +1,146 @@ +#include "FEPrefabInstanceSystem.h" +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetPrefabInstanceSystem() +{ + return FEPrefabInstanceSystem::GetInstancePointer(); +} +#endif + +FEPrefabInstanceSystem::FEPrefabInstanceSystem() +{ + RegisterOnComponentCallbacks(); + COMPONENTS_TOOL.RegisterComponentToJsonFunction(PrefabInstanceComponentToJson); + COMPONENTS_TOOL.RegisterComponentFromJsonFunction(PrefabInstanceComponentFromJson); +} + +void FEPrefabInstanceSystem::RegisterOnComponentCallbacks() +{ + SCENE_MANAGER.RegisterOnComponentConstructCallback(OnMyComponentAdded); + SCENE_MANAGER.RegisterOnComponentDestroyCallback(OnMyComponentDestroy); +} + +void FEPrefabInstanceSystem::OnMyComponentAdded(FEEntity* Entity) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FEPrefabInstanceComponent& PrefabInstanceComponent = Entity->GetComponent(); +} + +void FEPrefabInstanceSystem::DuplicatePrefabInstanceComponent(FEEntity* EntityToWorkWith, FEEntity* NewEntity) +{ + if (EntityToWorkWith == nullptr || NewEntity == nullptr || !EntityToWorkWith->HasComponent()) + return; + + FEPrefabInstanceComponent& PrefabInstanceComponent = EntityToWorkWith->GetComponent(); + +} + +void FEPrefabInstanceSystem::OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FEPrefabInstanceComponent& PrefabInstanceComponent = Entity->GetComponent(); +} + +FEPrefabInstanceSystem::~FEPrefabInstanceSystem() {}; + +bool FEPrefabInstanceSystem::IsPrefabInstanceUnmodified(FEEntity* Entity) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return false; + + FEPrefabInstanceComponent& PrefabInstanceComponent = Entity->GetComponent(); + FEPrefab* Prefab = PrefabInstanceComponent.GetPrefab(); + if (Prefab == nullptr) + return false; + + FEScene* EntityScene = Entity->GetParentScene(); + FENaiveSceneGraphNode* EntitySceneGraphNode = EntityScene->SceneGraph.GetNodeByEntityID(Entity->GetObjectID()); + + // Because root node in prefab scene is always empty, and in working scene it's not, we need to skip it + std::vector ChildNodesToCheck = EntitySceneGraphNode->GetChildren(); + std::vector PrefabChildNodesToCheck = Prefab->GetScene()->SceneGraph.GetRoot()->GetChildren(); + if (ChildNodesToCheck.size() != PrefabChildNodesToCheck.size()) + return false; + + for (size_t i = 0; i < PrefabChildNodesToCheck.size(); i++) + { + bool bHieraraichalEquivalence = SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(PrefabChildNodesToCheck[i], ChildNodesToCheck[i], true); + if (!bHieraraichalEquivalence) + return false; + + } + + // TODO: Check each node for equivalent components + + + + return true; +} + +bool FEPrefabInstanceSystem::IsPartOfPrefabInstance(FEEntity* Entity) +{ + return GetParentPrefabInstanceIfAny(Entity) != nullptr; +} + +FEEntity* FEPrefabInstanceSystem::GetParentPrefabInstanceIfAny(FEEntity* Entity) +{ + if (Entity == nullptr) + return nullptr; + + if (Entity->HasComponent()) + return Entity; + + FEScene* EntityScene = Entity->GetParentScene(); + FENaiveSceneGraphNode* EntitySceneGraphNode = EntityScene->SceneGraph.GetNodeByEntityID(Entity->GetObjectID()); + + FENaiveSceneGraphNode* ResultNode = EntityScene->SceneGraph.GetFirstParentNodeWithComponent(EntitySceneGraphNode); + if (ResultNode != nullptr) + return ResultNode->GetEntity(); + + return nullptr; +} + +Json::Value FEPrefabInstanceSystem::PrefabInstanceComponentToJson(FEEntity* Entity) +{ + Json::Value Root; + if (Entity == nullptr || !Entity->HasComponent()) + { + LOG.Add("FEPrefabInstanceSystem::PrefabInstanceComponentToJson Entity is nullptr or does not have FEPrefabInstanceComponent", "FE_LOG_ECS", FE_LOG_WARNING); + return Root; + } + FEPrefabInstanceComponent& PrefabInstanceComponent = Entity->GetComponent(); + + Root["PrefabID"] = PrefabInstanceComponent.Prefab->GetObjectID(); + + return Root; +} + +void FEPrefabInstanceSystem::PrefabInstanceComponentFromJson(FEEntity* Entity, Json::Value Root) +{ + if (Entity == nullptr) + { + LOG.Add("FEPrefabInstanceSystem::PrefabInstanceComponentFromJson Entity is nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + + std::string PrefabID = Root["PrefabID"].asString(); + if (PrefabID.empty()) + { + LOG.Add("FEPrefabInstanceSystem::PrefabInstanceComponentFromJson PrefabID is empty", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + + FEPrefab* Prefab = RESOURCE_MANAGER.GetPrefab(PrefabID); + if (Prefab == nullptr) + { + LOG.Add("FEPrefabInstanceSystem::PrefabInstanceComponentFromJson Prefab is nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + + Entity->AddComponent(Prefab); +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FEPrefabInstanceSystem.h b/SubSystems/Scene/Components/Systems/FEPrefabInstanceSystem.h new file mode 100644 index 0000000..fe0d6a0 --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FEPrefabInstanceSystem.h @@ -0,0 +1,37 @@ +#pragma once +#include "../../Scene/FESceneManager.h" + +namespace FocalEngine +{ + class FOCAL_ENGINE_API FEPrefabInstanceSystem + { + friend class FEScene; + friend class FERenderer; + friend class FEngine; + + SINGLETON_PRIVATE_PART(FEPrefabInstanceSystem) + + static void OnMyComponentAdded(FEEntity* Entity); + static void OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing); + void RegisterOnComponentCallbacks(); + + void DuplicatePrefabInstanceComponent(FEEntity* EntityToWorkWith, FEEntity* NewEntity); + + static Json::Value PrefabInstanceComponentToJson(FEEntity* Entity); + static void PrefabInstanceComponentFromJson(FEEntity* Entity, Json::Value Root); + public: + SINGLETON_PUBLIC_PART(FEPrefabInstanceSystem) + + bool IsPartOfPrefabInstance(FEEntity* Entity); + FEEntity* GetParentPrefabInstanceIfAny(FEEntity* Entity); + + bool IsPrefabInstanceUnmodified(FEEntity* Entity); + }; + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetPrefabInstanceSystem(); + #define PREFAB_INSTANCE_SYSTEM (*static_cast(GetPrefabInstanceSystem())) +#else + #define PREFAB_INSTANCE_SYSTEM FEPrefabInstanceSystem::GetInstance() +#endif +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FESkyDomeSystem.cpp b/SubSystems/Scene/Components/Systems/FESkyDomeSystem.cpp new file mode 100644 index 0000000..4d3a23a --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FESkyDomeSystem.cpp @@ -0,0 +1,118 @@ +#include "FESkyDomeSystem.h" +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetSkyDomeSystem() +{ + return FESkyDomeSystem::GetInstancePointer(); +} +#endif + +FEGameModel* FESkyDomeSystem::SkyDomeGameModel = nullptr; + +FESkyDomeSystem::FESkyDomeSystem() +{ + FEMaterial* SkyDomeMaterial = RESOURCE_MANAGER.CreateMaterial("skyDomeMaterial", "5A649B9E0F36073D4939313H"); + RESOURCE_MANAGER.SetTagIternal(SkyDomeMaterial, ENGINE_RESOURCE_TAG); + SkyDomeMaterial->Shader = RESOURCE_MANAGER.CreateShader("FESkyDome", RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//SkyDome//FE_SkyDome_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((RESOURCE_MANAGER.EngineFolder + "CoreExtensions//StandardMaterial//SkyDome//FE_SkyDome_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "3A69744E831A574E4857361B"); + RESOURCE_MANAGER.SetTagIternal(SkyDomeMaterial->Shader, ENGINE_RESOURCE_TAG); + + SkyDomeGameModel = new FEGameModel(RESOURCE_MANAGER.GetMesh("7F251E3E0D08013E3579315F"), RESOURCE_MANAGER.GetMaterial("5A649B9E0F36073D4939313H"/*"skyDomeMaterial"*/), "skyDomeGameModel"); + SkyDomeGameModel->SetID("17271E603508013IO77931TY"); + RESOURCE_MANAGER.SetTagIternal(SkyDomeGameModel, ENGINE_RESOURCE_TAG); + + RegisterOnComponentCallbacks(); + COMPONENTS_TOOL.RegisterComponentToJsonFunction(SkyDomeComponentToJson); + COMPONENTS_TOOL.RegisterComponentFromJsonFunction(SkyDomeComponentFromJson); + COMPONENTS_TOOL.RegisterComponentDuplicateFunction(DuplicateSkyDomeComponent); +} + +void FESkyDomeSystem::RegisterOnComponentCallbacks() +{ + SCENE_MANAGER.RegisterOnComponentConstructCallback(OnMyComponentAdded); + SCENE_MANAGER.RegisterOnComponentDestroyCallback(OnMyComponentDestroy); +} + +void FESkyDomeSystem::OnSceneClear() +{ + RegisterOnComponentCallbacks(); +} + +void FESkyDomeSystem::OnMyComponentAdded(FEEntity* Entity) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + Entity->AddComponent(SkyDomeGameModel); + Entity->GetComponent().SetVisibility(false); +} + +void FESkyDomeSystem::OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing) +{ + if (bIsSceneClearing) + return; + + if (Entity == nullptr || !Entity->HasComponent()) + return; +} + +void FESkyDomeSystem::DuplicateSkyDomeComponent(FEEntity* SourceEntity, FEEntity* TargetEntity) +{ + if (SourceEntity == nullptr || TargetEntity == nullptr) + return; + + if (!SourceEntity->HasComponent()) + return; + + // TODO: Make sky dome component not depend on game model component + // Currently, we will support duplication of sky dome component only if targer entity is in a different scene. + if (SourceEntity->GetParentScene() != TargetEntity->GetParentScene()) + TargetEntity->AddComponent(); +} + +FESkyDomeSystem::~FESkyDomeSystem() {}; + +bool FESkyDomeSystem::IsEnabled() +{ + return bEnabled; +} + +void FESkyDomeSystem::SetEnabled(bool NewValue) +{ + bEnabled = NewValue; +} + +Json::Value FESkyDomeSystem::SkyDomeComponentToJson(FEEntity* Entity) +{ + Json::Value Root; + if (Entity == nullptr || !Entity->HasComponent()) + { + LOG.Add("FESkyDomeSystem::SkyDomeComponentToJson Entity is nullptr or does not have FESkyDomeComponent", "FE_LOG_ECS", FE_LOG_WARNING); + return Root; + } + FESkyDomeComponent& SkyDomeComponent = Entity->GetComponent(); + + Root["Enabled"] = SKY_DOME_SYSTEM.bEnabled; + + return Root; +} + +void FESkyDomeSystem::SkyDomeComponentFromJson(FEEntity* Entity, Json::Value Root) +{ + if (Entity == nullptr) + { + LOG.Add("FESkyDomeSystem::SkyDomeComponentFromJson Entity is nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + + // TODO: Make sky dome component not depend on game model component + // GameModelComponent could load faster, so we need to remove it + if (Entity->HasComponent()) + Entity->RemoveComponent(); + + Entity->AddComponent(); + FESkyDomeComponent& SkyDomeComponent = Entity->GetComponent(); +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FESkyDomeSystem.h b/SubSystems/Scene/Components/Systems/FESkyDomeSystem.h new file mode 100644 index 0000000..8fe512a --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FESkyDomeSystem.h @@ -0,0 +1,39 @@ +#pragma once +#include "../../Scene/FESceneManager.h" + +namespace FocalEngine +{ + class FOCAL_ENGINE_API FESkyDomeSystem + { + friend class FEScene; + friend class FERenderer; + friend class FEngine; + + SINGLETON_PRIVATE_PART(FESkyDomeSystem) + + static void OnMyComponentAdded(FEEntity* Entity); + static void OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing); + void RegisterOnComponentCallbacks(); + void OnSceneClear(); + + static void DuplicateSkyDomeComponent(FEEntity* SourceEntity, FEEntity* TargetEntity); + + static FEGameModel* SkyDomeGameModel; + bool bEnabled = true; + + static Json::Value SkyDomeComponentToJson(FEEntity* Entity); + static void SkyDomeComponentFromJson(FEEntity* Entity, Json::Value Root); + public: + SINGLETON_PUBLIC_PART(FESkyDomeSystem) + + bool IsEnabled(); + void SetEnabled(bool NewValue); + }; + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetSkyDomeSystem(); + #define SKY_DOME_SYSTEM (*static_cast(GetSkyDomeSystem())) +#else + #define SKY_DOME_SYSTEM FESkyDomeSystem::GetInstance() +#endif +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FETerrainSystem.cpp b/SubSystems/Scene/Components/Systems/FETerrainSystem.cpp new file mode 100644 index 0000000..8649e54 --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FETerrainSystem.cpp @@ -0,0 +1,1443 @@ +#include "FETerrainSystem.h" +#include "../../Renderer/FERenderer.h" +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetTerrainSystem() +{ + return FETerrainSystem::GetInstancePointer(); +} +#endif + +FETerrainSystem::FETerrainSystem() +{ + std::string EngineFolder = RESOURCE_MANAGER.EngineFolder; + FEShader* TerrainShader = RESOURCE_MANAGER.CreateShader("FETerrainShader", RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//FE_Terrain_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//FE_Terrain_FS_GBUFFER.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//FE_Terrain_TCS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//FE_Terrain_TES.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//FE_Terrain_GS.glsl").c_str()).c_str(), + nullptr, + "5A3E4F5C13115856401F1D1C"); + + RESOURCE_MANAGER.SetTagIternal(TerrainShader, ENGINE_RESOURCE_TAG); + + FEShader* ShadowMapTerrainShader = RESOURCE_MANAGER.CreateShader("FESMTerrainShader", RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//ShadowMapShader//FE_SMTerrain_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//ShadowMapShader//FE_SMTerrain_FS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//ShadowMapShader//FE_SMTerrain_TCS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//ShadowMapShader//FE_SMTerrain_TES.glsl").c_str()).c_str(), + nullptr, nullptr, + "50064D3C4D0B537F0846274F"); + RESOURCE_MANAGER.SetTagIternal(ShadowMapTerrainShader, ENGINE_RESOURCE_TAG); + ShadowMapTerrainShader->UpdateUniformData("baseColor", glm::vec3(1.0f, 1.0f, 1.0f)); + RESOURCE_MANAGER.SetTagIternal(ShadowMapTerrainShader, ENGINE_RESOURCE_TAG); + + FEShader* TerrainBrushOutput = RESOURCE_MANAGER.CreateShader("terrainBrushOutput", RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_BrushOutput_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_BrushOutput_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "49654A4A10604C2A1221426B"); + RESOURCE_MANAGER.SetTagIternal(TerrainBrushOutput, ENGINE_RESOURCE_TAG); + + FEShader* TerrainBrushVisual = RESOURCE_MANAGER.CreateShader("terrainBrushVisual", RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_BrushVisual_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_BrushVisual_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "40064B7B4287805B296E526E"); + RESOURCE_MANAGER.SetTagIternal(TerrainBrushVisual, ENGINE_RESOURCE_TAG); + + FEShader* TerrainLayersNormalize = RESOURCE_MANAGER.CreateShader("terrainLayersNormalize", RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_BrushOutput_VS.glsl").c_str()).c_str(), + RESOURCE_MANAGER.LoadGLSL((EngineFolder + "CoreExtensions//StandardMaterial//TerrainMaterial//EditTools//FE_LayersNormalize_FS.glsl").c_str()).c_str(), + nullptr, nullptr, nullptr, nullptr, + "19294C00394A346A576F401C"); + RESOURCE_MANAGER.SetTagIternal(TerrainLayersNormalize, ENGINE_RESOURCE_TAG); + + BrushOutputShader = RESOURCE_MANAGER.GetShader("49654A4A10604C2A1221426B"/*"terrainBrushOutput"*/); + LayersNormalizeShader = RESOURCE_MANAGER.GetShader("19294C00394A346A576F401C"/*"terrainLayersNormalize"*/); + BrushVisualShader = RESOURCE_MANAGER.GetShader("40064B7B4287805B296E526E"/*"terrainBrushVisual"*/); + + PlaneMesh = RESOURCE_MANAGER.GetMesh("1Y251E6E6T78013635793156"/*"plane"*/); + + RegisterOnComponentCallbacks(); + COMPONENTS_TOOL.RegisterComponentToJsonFunction(TerrainComponentToJson); + COMPONENTS_TOOL.RegisterComponentFromJsonFunction(TerrainComponentFromJson); + COMPONENTS_TOOL.RegisterComponentDuplicateFunction(DuplicateTerrainComponent); +} + +void FETerrainSystem::RegisterOnComponentCallbacks() +{ + SCENE_MANAGER.RegisterOnComponentConstructCallback(OnMyComponentAdded); + SCENE_MANAGER.RegisterOnComponentDestroyCallback(OnMyComponentDestroy); +} + +void FETerrainSystem::OnMyComponentAdded(FEEntity* Entity) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FETerrainComponent& TerrainComponent = Entity->GetComponent(); + TerrainComponent.Shader = RESOURCE_MANAGER.GetShaderByName("FETerrainShader")[0]; +} + +void FETerrainSystem::DuplicateTerrainComponent(FEEntity* SourceEntity, FEEntity* TargetEntity) +{ + if (SourceEntity == nullptr || TargetEntity == nullptr) + return; + + if (!SourceEntity->HasComponent()) + return; + + FETerrainComponent& OriginalTerrainComponent = SourceEntity->GetComponent(); + TargetEntity->AddComponent(); + + FETerrainComponent& NewTerrainComponent = TargetEntity->GetComponent(); + NewTerrainComponent = OriginalTerrainComponent; + + for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + { + if (OriginalTerrainComponent.Layers[i] != nullptr) + { + NewTerrainComponent.Layers[i] = new FETerrainLayer(OriginalTerrainComponent.Layers[i]->GetName()); + NewTerrainComponent.Layers[i]->Material = OriginalTerrainComponent.Layers[i]->Material; + } + } + + TERRAIN_SYSTEM.InitTerrainEditTools(TargetEntity); +} + +void FETerrainSystem::OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FETerrainComponent& TerrainComponent = Entity->GetComponent(); + for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + { + delete TerrainComponent.Layers[i]; + TerrainComponent.Layers[i] = nullptr; + } + + FE_GL_ERROR(glDeleteBuffers(1, &TerrainComponent.GPULayersDataBuffer)); + + if (bIsSceneClearing) + return; + + for (size_t i = 0; i < TerrainComponent.SnapedInstancedEntities.size(); i++) + { + FEInstancedComponent& InstancedComponent = TerrainComponent.SnapedInstancedEntities[i]->GetComponent(); + InstancedComponent.UnSnapFromTerrain(); + } + TerrainComponent.SnapedInstancedEntities.clear(); +} + +FETerrainSystem::~FETerrainSystem() {}; + +FEAABB FETerrainSystem::GetAABB(FEEntity* TerrainEntity) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + return FEAABB(); + + FETransformComponent& TransformComponent = TerrainEntity->GetComponent(); + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (TransformComponent.IsDirty() || TerrainComponent.bDirtyFlag) + { + if (TerrainComponent.bDirtyFlag) + TerrainComponent.bDirtyFlag = true; + + if (TransformComponent.IsDirty()) + TransformComponent.SetDirtyFlag(false); + + FEAABB Result = TerrainComponent.AABB; + // -0.5f it is a little hack, because this -0.5f should be made during tessellation. + glm::vec3 Min = glm::vec3(-32.0f - 0.5f, TerrainComponent.AABB.GetMin()[1], -32.0f - 0.5f); + glm::vec3 Max = glm::vec3(32.0f + 64.0f * (TerrainComponent.ChunkPerSide - 1) - 0.5f, TerrainComponent.AABB.GetMax()[1], 32.0f + 64.0f * (TerrainComponent.ChunkPerSide - 1) - 0.5f); + Result = FEAABB(Min, Max); + Result = FEAABB(glm::vec3(Result.GetMin()[0], Result.GetMin()[1] * 2 * TerrainComponent.HightScale - TerrainComponent.HightScale, Result.GetMin()[2]), + glm::vec3(Result.GetMax()[0], Result.GetMax()[1] * 2 * TerrainComponent.HightScale - TerrainComponent.HightScale, Result.GetMax()[2])); + + TerrainComponent.FinalAABB = Result.Transform(TransformComponent.GetWorldMatrix()); + + TerrainComponent.XSize = TerrainComponent.FinalAABB.GetMax()[0] - TerrainComponent.FinalAABB.GetMin()[0]; + TerrainComponent.ZSize = TerrainComponent.FinalAABB.GetMax()[2] - TerrainComponent.FinalAABB.GetMin()[2]; + } + + return TerrainComponent.FinalAABB; +} + +FEAABB FETerrainSystem::GetPureAABB(FEEntity* TerrainEntity) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + return FEAABB(); + + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + FEAABB Result = TerrainComponent.AABB; + glm::vec3 Min = glm::vec3(-32.0f, TerrainComponent.AABB.GetMin()[1], -32.0f); + glm::vec3 Max = glm::vec3(32.0f + 64.0f * (TerrainComponent.ChunkPerSide - 1), TerrainComponent.AABB.GetMax()[1], 32.0f + 64.0f * (TerrainComponent.ChunkPerSide - 1)); + Result = FEAABB(Min, Max); + Result = FEAABB(glm::vec3(Result.GetMin()[0], Result.GetMin()[1] * 2 * TerrainComponent.HightScale - TerrainComponent.HightScale, Result.GetMin()[2]), + glm::vec3(Result.GetMax()[0], Result.GetMax()[1] * 2 * TerrainComponent.HightScale - TerrainComponent.HightScale, Result.GetMax()[2])); + + return Result; +} + +// **************************** TERRAIN EDITOR TOOLS **************************** +bool FETerrainSystem::IsBrushActive() +{ + return bBrushActive; +} + +void FETerrainSystem::SetBrushActive(const bool NewValue) +{ + bBrushActive = NewValue; +} + +FE_TERRAIN_BRUSH_MODE FETerrainSystem::GetBrushMode() +{ + return BrushMode; +} + +void FETerrainSystem::SetBrushMode(FEEntity* TerrainEntity, const FE_TERRAIN_BRUSH_MODE NewValue) +{ + if (NewValue == FE_TERRAIN_BRUSH_NONE) + { + TerrainEntityIDWithBrushModeOn = ""; + } + else + { + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + return; + + TerrainEntityIDWithBrushModeOn = TerrainEntity->GetObjectID(); + } + + BrushMode = NewValue; +} + +float FETerrainSystem::GetBrushSize() +{ + return BrushSize; +} + +void FETerrainSystem::SetBrushSize(float NewValue) +{ + if (NewValue <= 0.0f) + NewValue = 0.001f; + + if (NewValue > 1000.0f) + NewValue = 1000.0f; + + BrushSize = NewValue; +} + +float FETerrainSystem::GetBrushIntensity() +{ + return BrushIntensity; +} + +void FETerrainSystem::SetBrushIntensity(float NewValue) +{ + if (NewValue <= 0.0f) + NewValue = 0.001f; + + if (NewValue > 1000.0f) + NewValue = 1000.0f; + + BrushIntensity = NewValue; +} + +size_t FETerrainSystem::GetBrushLayerIndex() +{ + return BrushLayerIndex; +} + +void FETerrainSystem::SetBrushLayerIndex(const size_t NewValue) +{ + if (NewValue >= FE_TERRAIN_MAX_LAYERS) + return; + + BrushLayerIndex = NewValue; +} + +// ********************************** PointOnTerrain ********************************** +float FETerrainSystem::GetHeightAt(FEEntity* TerrainEntity, glm::vec2 XZWorldPosition) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + return -FLT_MAX; + + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + float LocalX = XZWorldPosition[0]; + float LocalZ = XZWorldPosition[1]; + + LocalX -= TerrainComponent.FinalAABB.GetMin()[0]; + LocalZ -= TerrainComponent.FinalAABB.GetMin()[2]; + + if (TerrainComponent.XSize == 0 || TerrainComponent.ZSize == 0) + GetAABB(TerrainEntity); + + LocalX = LocalX / TerrainComponent.XSize; + LocalZ = LocalZ / TerrainComponent.ZSize; + + if (LocalX > 0 && LocalZ > 0 && LocalX < 1.0 && LocalZ < 1.0) + { + LocalX = static_cast(static_cast(LocalX * TerrainComponent.HeightMap->GetWidth())); + LocalZ = static_cast(static_cast(LocalZ * TerrainComponent.HeightMap->GetHeight())); + + const int Index = static_cast(LocalZ * TerrainComponent.HeightMap->GetWidth() + LocalX); + return (TerrainComponent.HeightMapArray[Index] * 2 * TerrainComponent.HightScale - TerrainComponent.HightScale) * TerrainEntity->GetComponent().GetScale()[1] + TerrainEntity->GetComponent().GetPosition()[1]; + } + + return -FLT_MAX; +} + +glm::dvec3 FETerrainSystem::GetPointOnTerrain(FEEntity* TerrainEntity, const glm::dvec3 MouseRayStart, const glm::dvec3 MouseRayDirection, const float StartDistance, const float EndDistance) +{ + if (IntersectionInRange(TerrainEntity, StartDistance, EndDistance, MouseRayStart, MouseRayDirection)) + { + const glm::dvec3 PointOnTerrain = BinarySearch(TerrainEntity, 0, StartDistance, EndDistance, MouseRayStart, MouseRayDirection); + // if point is not above terrain, point could be above because isUnderGround returning true if we go out of terrain bounds to fix some bugs. + if ((GetHeightAt(TerrainEntity, glm::vec2(PointOnTerrain.x, PointOnTerrain.z)) + 1.0f) > PointOnTerrain.y) + return PointOnTerrain; + + return glm::dvec3(FLT_MAX); + } + + return glm::dvec3(FLT_MAX); +} + +bool FETerrainSystem::IsUnderGround(FEEntity* TerrainEntity, const glm::dvec3 TestPoint) +{ + const float Height = GetHeightAt(TerrainEntity, glm::vec2(TestPoint.x, TestPoint.z)); + // If we go outside terrain. + if (Height == -FLT_MAX) + return true; + + return TestPoint.y < Height ? true : false; +} + +glm::dvec3 FETerrainSystem::GetPointOnRay(FEEntity* TerrainEntity, const glm::dvec3 MouseRayStart, const glm::dvec3 MouseRayDirection, const float Distance) +{ + const glm::dvec3 Start = glm::dvec3(MouseRayStart.x, MouseRayStart.y, MouseRayStart.z); + const glm::dvec3 ScaledRay = glm::dvec3(MouseRayDirection.x * Distance, MouseRayDirection.y * Distance, MouseRayDirection.z * Distance); + + return Start + ScaledRay; +} + +bool FETerrainSystem::IntersectionInRange(FEEntity* TerrainEntity, const float Start, const float Finish, const glm::dvec3 MouseRayStart, const glm::dvec3 MouseRayDirection) +{ + const glm::dvec3 StartPoint = GetPointOnRay(TerrainEntity, MouseRayStart, MouseRayDirection, Start); + const glm::dvec3 EndPoint = GetPointOnRay(TerrainEntity, MouseRayStart, MouseRayDirection, Finish); + return !IsUnderGround(TerrainEntity, StartPoint) && IsUnderGround(TerrainEntity, EndPoint) ? true : false; +} + +glm::dvec3 FETerrainSystem::BinarySearch(FEEntity* TerrainEntity, const int Count, const float Start, const float Finish, const glm::dvec3 MouseRayStart, const glm::dvec3 MouseRayDirection) +{ + const int RecursionCount = 200; + const float Half = Start + ((Finish - Start) / 2.0f); + if (Count >= RecursionCount) + { + const glm::dvec3 EndPoint = GetPointOnRay(TerrainEntity, MouseRayStart, MouseRayDirection, Half); + if (GetHeightAt(TerrainEntity, glm::vec2(EndPoint.x, EndPoint.z)) != -1.0f) + { + return EndPoint; + } + else + { + return glm::dvec3(FLT_MAX); + } + } + + if (IntersectionInRange(TerrainEntity, Start, Half, MouseRayStart, MouseRayDirection)) + { + return BinarySearch(TerrainEntity, Count + 1, Start, Half, MouseRayStart, MouseRayDirection); + } + else + { + return BinarySearch(TerrainEntity, Count + 1, Half, Finish, MouseRayStart, MouseRayDirection); + } +} + +void FETerrainSystem::SnapInstancedEntity(FEEntity* TerrainEntity, FEEntity* EntityToSnap) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::SnapInstancedEntity TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + + if (!EntityToSnap->HasComponent()) + { + LOG.Add("FETerrainSystem::SnapInstancedEntity EntityToSnap does not have FEInstancedComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + + EntityToSnap->GetComponent().SnapToTerrain(TerrainEntity); + TerrainEntity->GetComponent().SnapedInstancedEntities.push_back(EntityToSnap); +} + +// There should be third system to manage that. +#include "../SubSystems/Scene/Components/Systems/FEInstancedSystem.h" +void FETerrainSystem::UpdateSnapedInstancedEntities(FEEntity* TerrainEntity) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::SnapInstancedEntity TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + for (size_t i = 0; i < TerrainComponent.SnapedInstancedEntities.size(); i++) + { + if (TerrainComponent.SnapedInstancedEntities[i] == nullptr) + continue; + + // If entity is still snapped. + if (TerrainComponent.SnapedInstancedEntities[i]->GetComponent().GetSnappedToTerrain() != TerrainEntity) + continue; + + INSTANCED_RENDERING_SYSTEM.ClearInstance(TerrainComponent.SnapedInstancedEntities[i]); + INSTANCED_RENDERING_SYSTEM.PopulateInstance(TerrainComponent.SnapedInstancedEntities[i], TerrainComponent.SnapedInstancedEntities[i]->GetComponent().SpawnInfo); + //SnapedInstancedEntities[i]->Clear(); + //SnapedInstancedEntities[i]->Populate(SnapedInstancedEntities[i]->SpawnInfo); + } +} + +void FETerrainSystem::UnSnapInstancedEntity(FEEntity* TerrainEntity, FEEntity* EntityToUnSnap) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::SnapInstancedEntity TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + for (size_t i = 0; i < TerrainComponent.SnapedInstancedEntities.size(); i++) + { + if (TerrainComponent.SnapedInstancedEntities[i] == EntityToUnSnap) + { + TerrainComponent.SnapedInstancedEntities.erase(TerrainComponent.SnapedInstancedEntities.begin() + i); + break; + } + } + + EntityToUnSnap->GetComponent().UnSnapFromTerrain(); +} + +void FETerrainSystem::UpdateBrush(const glm::dvec3 MouseRayStart, const glm::dvec3 MouseRayDirection) +{ + if (TerrainEntityIDWithBrushModeOn.empty()) + return; + + FEObject* TerrainObject = OBJECT_MANAGER.GetFEObject(TerrainEntityIDWithBrushModeOn); + if (TerrainObject == nullptr) + { + TerrainEntityIDWithBrushModeOn = ""; + return; + } + + FEEntity* TerrainEntityWithBrushModeOn = reinterpret_cast(OBJECT_MANAGER.GetFEObject(TerrainEntityIDWithBrushModeOn)); + FETerrainComponent& TerrainComponent = TerrainEntityWithBrushModeOn->GetComponent(); + + if (TerrainComponent.BrushVisualFB == nullptr) + return; + + if (GetBrushMode() == FE_TERRAIN_BRUSH_NONE) + { + if (bBrushVisualFBCleared) + return; + + // Get current clear color. + GLfloat BackgroundColor[4]; + glGetFloatv(GL_COLOR_CLEAR_VALUE, BackgroundColor); + + TerrainComponent.BrushVisualFB->Bind(); + FE_GL_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); + FE_GL_ERROR(glClear(GL_COLOR_BUFFER_BIT)); + TerrainComponent.BrushVisualFB->UnBind(); + bBrushVisualFBCleared = true; + + FE_GL_ERROR(glClearColor(BackgroundColor[0], BackgroundColor[1], BackgroundColor[2], BackgroundColor[3])); + + return; + } + + if (bCPUHeightInfoDirtyFlag) + { + if (std::chrono::duration_cast>(std::chrono::system_clock::now() - LastChangesTimeStamp).count() * 1000.0f > WaitBeforeUpdateMs) + { + bCPUHeightInfoDirtyFlag = false; + UpdateCPUHeightInfo(TerrainEntityWithBrushModeOn); + UpdateSnapedInstancedEntities(TerrainEntityWithBrushModeOn); + } + } + + const float Range = TerrainComponent.GetXSize() * 2.0f; + const glm::dvec3 CurrentTerrainPoint = GetPointOnTerrain(TerrainEntityWithBrushModeOn, MouseRayStart, MouseRayDirection, 0, Range); + + float LocalX = static_cast(CurrentTerrainPoint.x); + float LocalZ = static_cast(CurrentTerrainPoint.z); + + LocalX -= GetAABB(TerrainEntityWithBrushModeOn).GetMin()[0]; + LocalZ -= GetAABB(TerrainEntityWithBrushModeOn).GetMin()[2]; + + LocalX = LocalX / TerrainComponent.GetXSize(); + LocalZ = LocalZ / TerrainComponent.GetZSize(); + + if (IsBrushActive()) + { + LastChangesTimeStamp = std::chrono::system_clock::now(); + bCPUHeightInfoDirtyFlag = true; + TerrainComponent.HeightMap->SetDirtyFlag(true); + + if (LocalX > 0 && LocalZ > 0 && LocalX < 1.0 && LocalZ < 1.0) + { + BrushOutputShader->UpdateUniformData("brushCenter", glm::vec2(LocalX, LocalZ)); + } + + BrushOutputShader->UpdateUniformData("brushSize", BrushSize / (TerrainComponent.GetXSize() * 2.0f)); + BrushOutputShader->UpdateUniformData("brushMode", static_cast(GetBrushMode())); + + TerrainComponent.BrushOutputFB->SetColorAttachment(TerrainComponent.HeightMap); + BrushOutputShader->UpdateUniformData("brushIntensity", BrushIntensity / 10.0f); + + TerrainComponent.BrushOutputFB->Bind(); + BrushOutputShader->Start(); + + if (BrushMode == FE_TERRAIN_BRUSH_LAYER_DRAW) + { + TerrainComponent.LayerMaps[0]->Bind(0); + RENDERER.SetGLViewport(0, 0, TerrainComponent.LayerMaps[0]->GetWidth(), TerrainComponent.LayerMaps[0]->GetHeight()); + + BrushOutputShader->UpdateUniformData("brushIntensity", BrushIntensity * 5.0f); + BrushOutputShader->UpdateUniformData("layerIndex", static_cast(GetBrushLayerIndex())); + TerrainComponent.BrushOutputFB->SetColorAttachment(TerrainComponent.LayerMaps[0]); + BrushOutputShader->LoadUniformsDataToGPU(); + + FE_GL_ERROR(glBindVertexArray(PlaneMesh->GetVaoID())); + FE_GL_ERROR(glEnableVertexAttribArray(0)); + FE_GL_ERROR(glDrawElements(GL_TRIANGLES, PlaneMesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); + + TerrainComponent.LayerMaps[1]->Bind(0); + BrushOutputShader->UpdateUniformData("layerIndex", static_cast(GetBrushLayerIndex() - 4.0f)); + TerrainComponent.BrushOutputFB->SetColorAttachment(TerrainComponent.LayerMaps[1]); + BrushOutputShader->LoadUniformsDataToGPU(); + + FE_GL_ERROR(glDrawElements(GL_TRIANGLES, PlaneMesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); + FE_GL_ERROR(glDisableVertexAttribArray(0)); + FE_GL_ERROR(glBindVertexArray(0)); + + BrushOutputShader->Stop(); + + // Normalize all layers values to add up to 1.0 + LayersNormalizeShader->Start(); + TerrainComponent.LayerMaps[0]->Bind(0); + TerrainComponent.LayerMaps[1]->Bind(1); + + TerrainComponent.BrushOutputFB->SetColorAttachment(TerrainComponent.LayerMaps[0], 0); + TerrainComponent.BrushOutputFB->SetColorAttachment(TerrainComponent.LayerMaps[1], 1); + + FE_GL_ERROR(glBindVertexArray(PlaneMesh->GetVaoID())); + FE_GL_ERROR(glEnableVertexAttribArray(0)); + FE_GL_ERROR(glDrawElements(GL_TRIANGLES, PlaneMesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); + FE_GL_ERROR(glDisableVertexAttribArray(0)); + FE_GL_ERROR(glBindVertexArray(0)); + + LayersNormalizeShader->Stop(); + } + else + { + BrushOutputShader->LoadUniformsDataToGPU(); + TerrainComponent.HeightMap->Bind(0); + + RENDERER.SetGLViewport(0, 0, TerrainComponent.HeightMap->GetWidth(), TerrainComponent.HeightMap->GetHeight()); + + FE_GL_ERROR(glBindVertexArray(PlaneMesh->GetVaoID())); + FE_GL_ERROR(glEnableVertexAttribArray(0)); + FE_GL_ERROR(glDrawElements(GL_TRIANGLES, PlaneMesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); + FE_GL_ERROR(glDisableVertexAttribArray(0)); + FE_GL_ERROR(glBindVertexArray(0)); + + TerrainComponent.HeightMap->UnBind(); + } + + BrushOutputShader->Stop(); + TerrainComponent.BrushOutputFB->UnBind(); + } + + if (LocalX > 0 && LocalZ > 0 && LocalX < 1.0 && LocalZ < 1.0) + { + BrushVisualShader->UpdateUniformData("brushCenter", glm::vec2(LocalX, LocalZ)); + } + BrushVisualShader->UpdateUniformData("brushSize", BrushSize / (TerrainComponent.GetXSize() * 2.0f)); + + TerrainComponent.BrushVisualFB->Bind(); + BrushVisualShader->Start(); + + BrushVisualShader->LoadUniformsDataToGPU(); + RENDERER.SetGLViewport(0, 0, TerrainComponent.HeightMap->GetWidth(), TerrainComponent.HeightMap->GetHeight()); + + FE_GL_ERROR(glBindVertexArray(PlaneMesh->GetVaoID())); + FE_GL_ERROR(glEnableVertexAttribArray(0)); + FE_GL_ERROR(glDrawElements(GL_TRIANGLES, PlaneMesh->GetVertexCount(), GL_UNSIGNED_INT, 0)); + FE_GL_ERROR(glDisableVertexAttribArray(0)); + FE_GL_ERROR(glBindVertexArray(0)); + + BrushVisualShader->Stop(); + TerrainComponent.BrushVisualFB->UnBind(); + + bBrushVisualFBCleared = false; +} +// **************************** TERRAIN EDITOR TOOLS END **************************** + +void FETerrainSystem::UpdateCPUHeightInfo(FEEntity* TerrainEntity) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::SnapInstancedEntity TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + /*FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, heightMap->getTextureID())); + + size_t rawDataLenght = heightMap->getWidth() * heightMap->getHeight() * 2; + unsigned char* rawData = new unsigned char[rawDataLenght]; + glPixelStorei(GL_PACK_ALIGNMENT, 2); + FE_GL_ERROR(glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_SHORT, rawData)); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + heightMap->unBind();*/ + + size_t RawDataLenght; + unsigned char* RawData = TerrainComponent.HeightMap->GetRawData(&RawDataLenght); + + float Max = FLT_MIN; + float Min = FLT_MAX; + int Iterator = 0; + for (size_t i = 0; i < RawDataLenght; i += 2) + { + const unsigned short Temp = *(unsigned short*)(&RawData[i]); + TerrainComponent.HeightMapArray[Iterator] = Temp / static_cast(0xFFFF); + + if (Max < TerrainComponent.HeightMapArray[Iterator]) + Max = TerrainComponent.HeightMapArray[Iterator]; + + if (Min > TerrainComponent.HeightMapArray[Iterator]) + Min = TerrainComponent.HeightMapArray[Iterator]; + + Iterator++; + } + + const glm::vec3 MinPoint = glm::vec3(-1.0f, Min, -1.0f); + const glm::vec3 MaxPoint = glm::vec3(1.0f, Max, 1.0f); + TerrainComponent.AABB = FEAABB(MinPoint, MaxPoint); + FETransformComponent& Transform = TerrainEntity->GetComponent(); + Transform.SetDirtyFlag(true); + + delete[] RawData; +} + +void FETerrainSystem::ConnectInstancedEntityToLayer(FEEntity* TerrainEntity, FEEntity* EntityWithTerrainComponent, const int LayerIndex) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::SnapInstancedEntity TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + { + LOG.Add("FETerrainSystem::ConnectInstancedEntityToLayer with out of bound \"layerIndex\"", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + + if (TerrainComponent.Layers[LayerIndex] == nullptr) + { + LOG.Add("FETerrainSystem::ConnectInstancedEntityToLayer on indicated layer slot layer is nullptr", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + + if (!EntityWithTerrainComponent->HasComponent()) + { + LOG.Add("FETerrainSystem::ConnectInstancedEntityToLayer EntityWithTerrainComponent does not have FEInstancedComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + + for (size_t i = 0; i < TerrainComponent.SnapedInstancedEntities.size(); i++) + { + if (TerrainComponent.SnapedInstancedEntities[i]->GetObjectID() == EntityWithTerrainComponent->GetObjectID()) + { + //EntityWithTerrainComponent->GetComponent().ConnectToTerrainLayer(this, LayerIndex, &FETerrainComponent::GetLayerIntensityAt); + EntityWithTerrainComponent->GetComponent().ConnectToTerrainLayer(TerrainEntity, LayerIndex); + break; + } + } +} + +void FETerrainSystem::UnConnectInstancedEntityFromLayer(FEEntity* Entity) +{ + Entity->GetComponent().UnConnectFromTerrainLayer(); +} + +void FETerrainSystem::LoadHeightMap(std::string FileName, FEEntity* TerrainEntity) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::InitTerrainEditTools TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + FETexture* NewTexture = nullptr; + std::string FileExtension = FILE_SYSTEM.GetFileExtension(FileName); + if (FileExtension == ".texture") + { + NewTexture = RESOURCE_MANAGER.LoadFETexture(FileName.c_str()); + } + else if (FileExtension == ".png") + { + NewTexture = RESOURCE_MANAGER.LoadPNGTexture(FileName.c_str()); + } + else + { + LOG.Add("FETerrainSystem::LoadHeightMap FileExtension is not supported", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + + SetHeightMap(NewTexture, TerrainEntity); +} + +void FETerrainSystem::SetHeightMap(FETexture* HightMap, FEEntity* TerrainEntity) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::InitTerrainEditTools TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (HightMap == nullptr || HightMap->GetInternalFormat() != GL_R16) + return; + + FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, HightMap->GetTextureID())); + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + FE_GL_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + + size_t TextureDataSize = 0; + unsigned short* TextureData = reinterpret_cast(HightMap->GetRawData(&TextureDataSize)); + RESOURCE_MANAGER.SetTag(HightMap, TERRAIN_SYSTEM_RESOURCE_TAG); + if (TextureData == nullptr || TextureDataSize == 0) + return; + + size_t ElemntCount = TextureDataSize / sizeof(unsigned short); + TerrainComponent.HeightMapArray.resize(ElemntCount); + + float Max = FLT_MIN; + float Min = FLT_MAX; + for (size_t i = 0; i < ElemntCount; i++) + { + const unsigned short TemporaryValue = TextureData[i]; + TerrainComponent.HeightMapArray[i] = TemporaryValue / static_cast(0xFFFF); + + if (Max < TerrainComponent.HeightMapArray[i]) + Max = TerrainComponent.HeightMapArray[i]; + + if (Min > TerrainComponent.HeightMapArray[i]) + Min = TerrainComponent.HeightMapArray[i]; + } + + const glm::vec3 MinPoint = glm::vec3(-1.0f, Min, -1.0f); + const glm::vec3 MaxPoint = glm::vec3(1.0f, Max, 1.0f); + TerrainComponent.AABB = FEAABB(MinPoint, MaxPoint); + + TerrainComponent.HeightMap = HightMap; + InitTerrainEditTools(TerrainEntity); + UpdateCPUHeightInfo(TerrainEntity); +} + +void FETerrainSystem::InitTerrainEditTools(FEEntity* TerrainEntity) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::InitTerrainEditTools TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + TerrainComponent.BrushOutputFB = RESOURCE_MANAGER.CreateFramebuffer(FE_COLOR_ATTACHMENT, 32, 32); + delete TerrainComponent.BrushOutputFB->GetColorAttachment(); + TerrainComponent.BrushOutputFB->SetColorAttachment(TerrainComponent.HeightMap); + + TerrainComponent.BrushVisualFB = RESOURCE_MANAGER.CreateFramebuffer(FE_COLOR_ATTACHMENT, TerrainComponent.HeightMap->GetWidth(), TerrainComponent.HeightMap->GetHeight()); + TerrainComponent.ProjectedMap = TerrainComponent.BrushVisualFB->GetColorAttachment(); +} + +bool FETerrainSystem::UpdateLayerMapsRawData(FEEntity* TerrainEntity) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::UpdateLayerMapsRawData TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return false; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (TIME.EndTimeStamp(TerrainEntity->GetObjectID()) != -1.0) + { + if (TIME.EndTimeStamp(TerrainEntity->GetObjectID()) < 2000) + return false; + } + + TIME.BeginTimeStamp(TerrainEntity->GetObjectID()); + + for (size_t i = 0; i < TerrainComponent.LayerMaps.size(); i++) + { + if (TerrainComponent.LayerMapsRawData[i] != nullptr) + { + delete TerrainComponent.LayerMapsRawData[i]; + TerrainComponent.LayerMapsRawData[i] = nullptr; + } + + if (TerrainComponent.LayerMaps[i] == nullptr) + { + TerrainComponent.LayerMapsRawData[i] = nullptr; + } + else + { + TerrainComponent.LayerMapsRawData[i] = TerrainComponent.LayerMaps[i]->GetRawData(); + } + } + + return true; +} + +float FETerrainSystem::GetLayerIntensityAt(FEEntity* TerrainEntity, glm::vec2 XZWorldPosition, const int LayerIndex) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::GetLayerIntensityAt TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return 0.0f; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + { + LOG.Add("FETerrainSystem::GetLayerIntensityAt with out of bound \"layerIndex\"", "FE_LOG_GENERAL", FE_LOG_WARNING); + return 0.0f; + } + + if (TerrainComponent.Layers[LayerIndex] == nullptr) + { + LOG.Add("FETerrainSystem::GetLayerIntensityAt on indicated layer slot layer is nullptr", "FE_LOG_GENERAL", FE_LOG_WARNING); + return 0.0f; + } + + UpdateLayerMapsRawData(TerrainEntity); + + float LocalX = XZWorldPosition[0]; + float LocalZ = XZWorldPosition[1]; + + LocalX -= TerrainComponent.FinalAABB.GetMin()[0]; + LocalZ -= TerrainComponent.FinalAABB.GetMin()[2]; + + if (TerrainComponent.XSize == 0 || TerrainComponent.ZSize == 0) + GetAABB(TerrainEntity); + + LocalX = LocalX / TerrainComponent.XSize; + LocalZ = LocalZ / TerrainComponent.ZSize; + + if (LocalX > 0 && LocalZ > 0 && LocalX < 1.0 && LocalZ < 1.0) + { + const int TextureIndex = LayerIndex / FE_TERRAIN_LAYER_PER_TEXTURE; + FETexture* Texture = TerrainComponent.LayerMaps[TextureIndex]; + LocalX = static_cast(static_cast(LocalX * Texture->GetWidth())); + LocalZ = static_cast(static_cast(LocalZ * Texture->GetHeight())); + + const int Index = static_cast(LocalZ * Texture->GetWidth() + LocalX) * 4 + LayerIndex % FE_TERRAIN_LAYER_PER_TEXTURE; + + if (TerrainComponent.LayerMapsRawData[TextureIndex] != nullptr) + return TerrainComponent.LayerMapsRawData[TextureIndex][Index] / 255.0f; + + } + + return 0.0f; +} + +void FETerrainSystem::FillTerrainLayerMaskWithRawData(FEEntity* TerrainEntity, const unsigned char* RawData, const size_t LayerIndex) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::FillTerrainLayerMaskWithRawData TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (RawData == nullptr) + { + LOG.Add("FETerrainSystem::fillTerrainLayerMaskWithRawData with nullptr rawData", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + + if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + { + LOG.Add("FETerrainSystem::fillTerrainLayerMaskWithRawData with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + int Index = 0; + const size_t TextureWidht = TerrainComponent.LayerMaps[0]->GetWidth(); + const size_t TextureHeight = TerrainComponent.LayerMaps[0]->GetHeight(); + + std::vector LayersPerTextureData; + LayersPerTextureData.resize(2); + LayersPerTextureData[0] = TerrainComponent.LayerMaps[0]->GetRawData(); + LayersPerTextureData[1] = TerrainComponent.LayerMaps[1]->GetRawData(); + + std::vector LayersPerChannelData; + LayersPerChannelData.resize(FE_TERRAIN_MAX_LAYERS); + for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + { + LayersPerChannelData[i] = new unsigned char[TextureWidht * TextureHeight]; + } + + for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + { + Index = 0; + if (LayerIndex == i) + { + for (size_t j = 0; j < TextureWidht * TextureHeight; j++) + { + LayersPerChannelData[i][Index++] = RawData[j]; + } + } + else + { + for (size_t j = i % FE_TERRAIN_LAYER_PER_TEXTURE; j < TextureWidht * TextureHeight * 4; j += 4) + { + LayersPerChannelData[i][Index++] = LayersPerTextureData[i / FE_TERRAIN_LAYER_PER_TEXTURE][j]; + } + } + } + + std::vector FinalTextureChannels; + FinalTextureChannels.resize(2); + FinalTextureChannels[0] = new unsigned char[TextureWidht * TextureHeight * 4]; + FinalTextureChannels[1] = new unsigned char[TextureWidht * TextureHeight * 4]; + + Index = 0; + + int* AllChannelsPixels = new int[8]; + + for (size_t i = 0; i < TextureWidht * TextureHeight * 4; i += 4) + { + float sum = 0.0f; + for (size_t j = 0; j < 8; j++) + { + AllChannelsPixels[j] = LayersPerChannelData[j][Index]; + } + + FinalTextureChannels[0][i] = static_cast(AllChannelsPixels[0]); + FinalTextureChannels[0][i + 1] = static_cast(AllChannelsPixels[1]); + FinalTextureChannels[0][i + 2] = static_cast(AllChannelsPixels[2]); + FinalTextureChannels[0][i + 3] = static_cast(AllChannelsPixels[3]); + + FinalTextureChannels[1][i] = static_cast(AllChannelsPixels[4]); + FinalTextureChannels[1][i + 1] = static_cast(AllChannelsPixels[5]); + FinalTextureChannels[1][i + 2] = static_cast(AllChannelsPixels[6]); + FinalTextureChannels[1][i + 3] = static_cast(AllChannelsPixels[7]); + + Index++; + } + + const int MaxDimention = std::max(static_cast(TextureWidht), static_cast(TextureHeight)); + const size_t MipCount = static_cast(floor(log2(MaxDimention)) + 1); + + TerrainComponent.LayerMaps[0]->UpdateRawData(FinalTextureChannels[0], MipCount); + FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); + + TerrainComponent.LayerMaps[1]->UpdateRawData(FinalTextureChannels[1], MipCount); + FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); + + delete[] LayersPerTextureData[0]; + delete[] LayersPerTextureData[1]; + for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + { + delete[]LayersPerChannelData[i]; + } + + delete[] FinalTextureChannels[0]; + delete[] FinalTextureChannels[1]; + delete[] AllChannelsPixels; +} + +void FETerrainSystem::FillTerrainLayerMask(FEEntity* TerrainEntity, const size_t LayerIndex) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::FillTerrainLayerMask TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + { + LOG.Add("FETerrainSystem::fillTerrainLayerMask with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + if (TerrainComponent.Layers[LayerIndex] == nullptr) + { + LOG.Add("FETerrainSystem::fillTerrainLayerMask on indicated layer slot layer is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + FETexture* CorrectLayer = TerrainComponent.LayerMaps[LayerIndex / FE_TERRAIN_LAYER_PER_TEXTURE]; + const size_t TextureWidht = CorrectLayer->GetWidth(); + const size_t TextureHeight = CorrectLayer->GetHeight(); + unsigned char* FilledChannel = new unsigned char[TextureWidht * TextureHeight]; + for (size_t i = 0; i < TextureWidht * TextureHeight; i++) + { + FilledChannel[i] = 255; + } + + FillTerrainLayerMaskWithRawData(TerrainEntity, FilledChannel, LayerIndex); + delete[] FilledChannel; +} + +void FETerrainSystem::ActivateVacantLayerSlot(FEEntity* TerrainEntity, FEMaterial* Material) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::ActivateVacantLayerSlot TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + // If this terrain does not have LayerMaps we would create them. + if (TerrainComponent.LayerMaps[0] == nullptr) + { + /*FETexture* NewTexture = CreateTexture();*/ + int TextureWidth = FE_TERRAIN_STANDARD_LAYER_MAP_RESOLUTION; + int TextureHeight = FE_TERRAIN_STANDARD_LAYER_MAP_RESOLUTION; + //NewTexture->InternalFormat = GL_RGBA; + + std::vector RawData; + const size_t DataLenght = TextureWidth * TextureHeight * 4; + RawData.resize(DataLenght); + for (size_t i = 0; i < DataLenght; i++) + { + RawData[i] = 0; + } + + //FETexture* NewTexture = RESOURCE_MANAGER.RawDataToFETexture(RawData.data(), TextureWidth, TextureHeight, -1, GL_RGBA); + + /*FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); + FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, GL_RGBA, NewTexture->Width, NewTexture->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, RawData.data()); + + FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f));*/ + + TerrainComponent.LayerMaps[0] = RESOURCE_MANAGER.RawDataToFETexture(RawData.data(), TextureWidth, TextureHeight, -1, GL_RGBA); + TerrainComponent.LayerMaps[0]->SetName("Terrain_LayerMap0"); + RESOURCE_MANAGER.SetTag(TerrainComponent.LayerMaps[0], TERRAIN_SYSTEM_RESOURCE_TAG); + + /*NewTexture = CreateTexture(); + NewTexture->Width = FE_TERRAIN_STANDARD_LAYER_MAP_RESOLUTION; + NewTexture->Height = FE_TERRAIN_STANDARD_LAYER_MAP_RESOLUTION; + NewTexture->InternalFormat = GL_RGBA; + + FE_GL_ERROR(glBindTexture(GL_TEXTURE_2D, NewTexture->TextureID)); + FETexture::GPUAllocateTeture(GL_TEXTURE_2D, 0, GL_RGBA, NewTexture->Width, NewTexture->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, RawData.data()); + + FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f));*/ + + TerrainComponent.LayerMaps[1] = RESOURCE_MANAGER.RawDataToFETexture(RawData.data(), TextureWidth, TextureHeight, -1, GL_RGBA); + TerrainComponent.LayerMaps[1]->SetName("Terrain_LayerMap1"); + RESOURCE_MANAGER.SetTag(TerrainComponent.LayerMaps[1], TERRAIN_SYSTEM_RESOURCE_TAG); + + TerrainComponent.ActivateVacantLayerSlot(Material); + FillTerrainLayerMask(TerrainEntity, 0); + return; + } + + TerrainComponent.ActivateVacantLayerSlot(Material); +} + +void FETerrainSystem::ClearTerrainLayerMask(FEEntity* TerrainEntity, size_t LayerIndex) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::ClearTerrainLayerMask TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + { + LOG.Add("FEResourceManager::ClearTerrainLayerMask with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + if (TerrainComponent.Layers[LayerIndex] == nullptr) + { + LOG.Add("FEResourceManager::ClearTerrainLayerMask on indicated layer slot layer is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + FETexture* CorrectLayer = TerrainComponent.LayerMaps[LayerIndex / FE_TERRAIN_LAYER_PER_TEXTURE]; + const size_t TextureWidht = CorrectLayer->GetWidth(); + const size_t TextureHeight = CorrectLayer->GetHeight(); + unsigned char* FilledChannel = new unsigned char[TextureWidht * TextureHeight]; + for (size_t i = 0; i < TextureWidht * TextureHeight; i++) + { + FilledChannel[i] = 0; + } + + FillTerrainLayerMaskWithRawData(TerrainEntity, FilledChannel, LayerIndex); + delete[] FilledChannel; +} + +void FETerrainSystem::DeleteTerrainLayerMask(FEEntity* TerrainEntity, size_t LayerIndex) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::DeleteTerrainLayerMask TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_GENERAL", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + { + LOG.Add("FETerrainSystem::DeleteTerrainLayerMask with out of bound \"layerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + if (TerrainComponent.Layers[LayerIndex] == nullptr) + { + LOG.Add("FETerrainSystem::DeleteTerrainLayerMask on indicated layer slot layer is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + ClearTerrainLayerMask(TerrainEntity, LayerIndex); + + std::vector LayersPerTextureData; + LayersPerTextureData.resize(2); + size_t RawDataSize = 0; + LayersPerTextureData[0] = TerrainComponent.LayerMaps[0]->GetRawData(&RawDataSize); + LayersPerTextureData[1] = TerrainComponent.LayerMaps[1]->GetRawData(); + + std::vector AllLayers; + AllLayers.resize(8); + for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + { + AllLayers[i] = new unsigned char[RawDataSize / 4]; + } + + // Gathering channels from 2 textures. + int ChannelIndex = 0; + for (size_t i = 0; i < RawDataSize; i += 4) + { + AllLayers[0][ChannelIndex] = LayersPerTextureData[0][i]; + AllLayers[1][ChannelIndex] = LayersPerTextureData[0][i + 1]; + AllLayers[2][ChannelIndex] = LayersPerTextureData[0][i + 2]; + AllLayers[3][ChannelIndex] = LayersPerTextureData[0][i + 3]; + + AllLayers[4][ChannelIndex] = LayersPerTextureData[1][i]; + AllLayers[5][ChannelIndex] = LayersPerTextureData[1][i + 1]; + AllLayers[6][ChannelIndex] = LayersPerTextureData[1][i + 2]; + AllLayers[7][ChannelIndex] = LayersPerTextureData[1][i + 3]; + + ChannelIndex++; + } + + // Shifting existing layers masks to place where was deleted mask. + for (size_t i = LayerIndex; i < FE_TERRAIN_MAX_LAYERS - 1; i++) + { + for (size_t j = 0; j < RawDataSize / 4; j++) + { + AllLayers[i][j] = AllLayers[i + 1][j]; + } + } + + unsigned char* FirstTextureData = new unsigned char[RawDataSize]; + unsigned char* SecondTextureData = new unsigned char[RawDataSize]; + + // Putting individual channels back to 2 distinct textures. + ChannelIndex = 0; + for (size_t i = 0; i < RawDataSize; i += 4) + { + FirstTextureData[i] = AllLayers[0][ChannelIndex]; + FirstTextureData[i + 1] = AllLayers[1][ChannelIndex]; + FirstTextureData[i + 2] = AllLayers[2][ChannelIndex]; + FirstTextureData[i + 3] = AllLayers[3][ChannelIndex]; + + SecondTextureData[i] = AllLayers[4][ChannelIndex]; + SecondTextureData[i + 1] = AllLayers[5][ChannelIndex]; + SecondTextureData[i + 2] = AllLayers[6][ChannelIndex]; + SecondTextureData[i + 3] = AllLayers[7][ChannelIndex]; + + ChannelIndex++; + } + + const int MaxDimention = std::max(TerrainComponent.LayerMaps[0]->GetWidth(), TerrainComponent.LayerMaps[0]->GetHeight()); + const size_t MipCount = static_cast(floor(log2(MaxDimention)) + 1); + + TerrainComponent.LayerMaps[0]->UpdateRawData(FirstTextureData, MipCount); + FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); + + TerrainComponent.LayerMaps[1]->UpdateRawData(SecondTextureData, MipCount); + FE_GL_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f)); + FE_GL_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f)); + + for (size_t i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + { + delete[] AllLayers[i]; + } + + delete[] FirstTextureData; + delete[] SecondTextureData; + + TerrainComponent.DeleteLayerInSlot(LayerIndex); +} + +// This function is not working properly if mask needs to be resized. +void FETerrainSystem::LoadTerrainLayerMask(FEEntity* TerrainEntity, std::string FileName, const size_t LayerIndex) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::LoadTerrainLayerMask TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_LOADING", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + { + LOG.Add("FETerrainSystem::LoadTerrainLayerMask with out of bound \"layerIndex\"", "FE_LOG_LOADING", FE_LOG_WARNING); + return; + } + + if (TerrainComponent.Layers[LayerIndex] == nullptr) + { + LOG.Add("FETerrainSystem::LoadTerrainLayerMask on indicated layer slot layer is nullptr", "FE_LOG_LOADING", FE_LOG_WARNING); + return; + } + + FETexture* LoadedTexture = RESOURCE_MANAGER.LoadPNGTexture(FileName.c_str()); + size_t RawDataSize = 0; + unsigned char* RawData = LoadedTexture->GetRawData(&RawDataSize); + // It should be just ordinary png not gray scale. + if (RawDataSize != LoadedTexture->GetWidth() * LoadedTexture->GetHeight() * 4) + { + LOG.Add(std::string("Can't use file: ") + FileName + " in function FETerrainSystem::LoadTerrainLayerMask as a mask.", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + + // If new texture have different resolution. + FETexture* FirstLayerMap = TerrainComponent.LayerMaps[0]; + if (LoadedTexture->GetWidth() != FirstLayerMap->GetWidth() || LoadedTexture->GetHeight() != FirstLayerMap->GetHeight()) + { + bool bNeedToResizeMaskTexture = false; + // Firstly we check if current masks has any data. + std::vector LayersPerTextureData; + LayersPerTextureData.resize(2); + LayersPerTextureData[0] = TerrainComponent.LayerMaps[0]->GetRawData(); + LayersPerTextureData[1] = TerrainComponent.LayerMaps[1]->GetRawData(); + + // We fill first layer by default so we should check TextureIterator differently + const unsigned char FirstValue = LayersPerTextureData[0][0]; + for (size_t i = 0; i < static_cast(TerrainComponent.LayerMaps[0]->GetWidth() * TerrainComponent.LayerMaps[0]->GetHeight()); i += 4) + { + if (LayersPerTextureData[0][i] != FirstValue || LayersPerTextureData[0][i + 1] != 0 || + LayersPerTextureData[0][i + 2] != 0 || LayersPerTextureData[0][i + 3] != 0 || + LayersPerTextureData[1][i] != 0 || LayersPerTextureData[1][i + 1] != 0 || + LayersPerTextureData[1][i + 2] != 0 || LayersPerTextureData[1][i + 3] != 0) + { + bNeedToResizeMaskTexture = true; + break; + } + } + + if (bNeedToResizeMaskTexture) + { + LOG.Add("FETerrainSystem::LoadTerrainLayerMask resizing loaded mask to match currently used one.", "FE_LOG_LOADING", FE_LOG_WARNING); + unsigned char* NewRawData = RESOURCE_MANAGER.ResizeTextureRawData(RawData, LoadedTexture->GetWidth(), LoadedTexture->GetHeight(), FirstLayerMap->GetWidth(), FirstLayerMap->GetHeight(), GL_RGBA, 1); + if (NewRawData == nullptr) + { + LOG.Add("FETerrainSystem::LoadTerrainLayerMask resizing loaded mask failed.", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + + delete[] RawData; + RawData = NewRawData; + } + else + { + LOG.Add("FETerrainSystem::LoadTerrainLayerMask resizing TerrainLayerMap to match currently loaded one.", "FE_LOG_LOADING", FE_LOG_WARNING); + + // Fix problem, both LayerMaps would be cleared. + std::vector RawData; + const size_t DataLenght = LoadedTexture->GetWidth() * LoadedTexture->GetHeight() * 4; + RawData.resize(DataLenght); + for (size_t i = 0; i < DataLenght; i++) + { + RawData[i] = 0; + } + + FETexture* NewTexture = RESOURCE_MANAGER.RawDataToFETexture(RawData.data(), LoadedTexture->GetWidth(), LoadedTexture->GetHeight(), GL_RGBA, GL_RGBA); + + RESOURCE_MANAGER.DeleteFETexture(TerrainComponent.LayerMaps[0]); + TerrainComponent.LayerMaps[0] = NewTexture; + FirstLayerMap = TerrainComponent.LayerMaps[0]; + + NewTexture = RESOURCE_MANAGER.RawDataToFETexture(RawData.data(), LoadedTexture->GetWidth(), LoadedTexture->GetHeight(), GL_RGBA, GL_RGBA); + + RESOURCE_MANAGER.DeleteFETexture(TerrainComponent.LayerMaps[1]); + TerrainComponent.LayerMaps[1] = NewTexture; + } + } + + unsigned char* FilledChannel = new unsigned char[FirstLayerMap->GetWidth() * FirstLayerMap->GetHeight()]; + int Index = 0; + for (size_t i = 0; i < static_cast(FirstLayerMap->GetWidth() * FirstLayerMap->GetHeight() * 4); i += 4) + { + FilledChannel[Index++] = RawData[i]; + } + + FillTerrainLayerMaskWithRawData(TerrainEntity, FilledChannel, LayerIndex); +} + +void FETerrainSystem::SaveTerrainLayerMask(FEEntity* TerrainEntity, std::string FileName, const size_t LayerIndex) +{ + if (TerrainEntity == nullptr || !TerrainEntity->HasComponent()) + { + LOG.Add("FETerrainSystem::SaveTerrainLayerMask TerrainEntity is nullptr or does not have FETerrainComponent", "FE_LOG_LOADING", FE_LOG_WARNING); + return; + } + FETerrainComponent& TerrainComponent = TerrainEntity->GetComponent(); + + if (LayerIndex < 0 || LayerIndex >= FE_TERRAIN_MAX_LAYERS) + { + LOG.Add("FETerrainSystem::SaveTerrainLayerMask with out of bound \"LayerIndex\"", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + if (TerrainComponent.Layers[LayerIndex] == nullptr) + { + LOG.Add("FETerrainSystem::SaveTerrainLayerMask \"TerrainComponent.Layers[LayerIndex]\" is nullptr", "FE_LOG_RENDERING", FE_LOG_WARNING); + return; + } + + // Reading data from current layer map texture. + size_t ResultingTextureDataLenght = 0; + FETexture* CorrectLayer = TerrainComponent.LayerMaps[LayerIndex / FE_TERRAIN_LAYER_PER_TEXTURE]; + const unsigned char* RawData = CorrectLayer->GetRawData(&ResultingTextureDataLenght); + unsigned char* ResultingData = new unsigned char[ResultingTextureDataLenght]; + + for (size_t i = 0; i < ResultingTextureDataLenght; i += 4) + { + const size_t index = i + LayerIndex % FE_TERRAIN_LAYER_PER_TEXTURE; + ResultingData[i] = RawData[index]; + ResultingData[i + 1] = RawData[index]; + ResultingData[i + 2] = RawData[index]; + ResultingData[i + 3] = 255; + } + + RESOURCE_MANAGER.ExportRawDataToPNG(FileName.c_str(), ResultingData, CorrectLayer->GetWidth(), CorrectLayer->GetHeight(), GL_RGBA); +} + +Json::Value FETerrainSystem::TerrainComponentToJson(FEEntity* Entity) +{ + Json::Value Root; + if (Entity == nullptr || !Entity->HasComponent()) + { + LOG.Add("FETerrainSystem::TerrainComponentToJson Entity is nullptr or does not have FETerrainComponent", "FE_LOG_ECS", FE_LOG_WARNING); + return Root; + } + FETerrainComponent& TerrainComponent = Entity->GetComponent(); + + Root["Height map"]["ID"] = TerrainComponent.HeightMap->GetObjectID(); + Root["Height map"]["Name"] = TerrainComponent.HeightMap->GetName(); + Root["Height map"]["FileName"] = TerrainComponent.HeightMap->GetObjectID() + ".texture"; + + Root["Hight scale"] = TerrainComponent.GetHightScale(); + Root["Displacement scale"] = TerrainComponent.GetDisplacementScale(); + Root["Tile multiplicator"]["X"] = TerrainComponent.GetTileMult().x; + Root["Tile multiplicator"]["Y"] = TerrainComponent.GetTileMult().y; + Root["LODlevel"] = TerrainComponent.GetLODLevel(); + Root["Chunks per side"] = TerrainComponent.GetChunkPerSide(); + + // Saving terrains layers. + for (int i = 0; i < TerrainComponent.LayerMaps.size(); i++) + { + if (TerrainComponent.LayerMaps[i] != nullptr) + { + Root["LayerMaps"][i]["ID"] = TerrainComponent.LayerMaps[i]->GetObjectID(); + Root["LayerMaps"][i]["Name"] = TerrainComponent.LayerMaps[i]->GetName(); + Root["LayerMaps"][i]["FileName"] = TerrainComponent.LayerMaps[i]->GetObjectID() + ".texture"; + } + } + + for (int i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + { + FETerrainLayer* CurrentLayer = TerrainComponent.GetLayerInSlot(i); + if (CurrentLayer == nullptr) + { + Root["Layers"][i]["isAcive"] = false; + break; + } + + Root["Layers"][i]["isAcive"] = true; + Root["Layers"][i]["Name"] = CurrentLayer->GetMaterial()->GetName(); + Root["Layers"][i]["Material ID"] = CurrentLayer->GetMaterial()->GetObjectID(); + } + + return Root; +} + +void FETerrainSystem::TerrainComponentFromJson(FEEntity* Entity, Json::Value Root) +{ + if (Entity == nullptr) + { + LOG.Add("FETerrainSystem::TerrainComponentFromJson Entity is nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + + Entity->AddComponent(); + FETerrainComponent& TerrainComponent = Entity->GetComponent(); + + FETexture* HightMapTexture = RESOURCE_MANAGER.GetTexture(Root["Height map"]["ID"].asString()); + TERRAIN_SYSTEM.SetHeightMap(HightMapTexture, Entity); + + TerrainComponent.SetHightScale(Root["Hight scale"].asFloat()); + TerrainComponent.SetDisplacementScale(Root["Displacement scale"].asFloat()); + TerrainComponent.SetTileMult(glm::vec2(Root["Tile multiplicator"]["X"].asFloat(), Root["Tile multiplicator"]["Y"].asFloat())); + TerrainComponent.SetLODLevel(Root["LODlevel"].asFloat()); + TerrainComponent.SetChunkPerSide(Root["Chunks per side"].asFloat()); + + for (int i = 0; i < FE_TERRAIN_MAX_LAYERS / FE_TERRAIN_LAYER_PER_TEXTURE; i++) + { + if (Root.isMember("LayerMaps")) + { + FETexture* LayerTexture = RESOURCE_MANAGER.GetTexture(Root["LayerMaps"][i]["ID"].asString()); + TerrainComponent.LayerMaps[i] = LayerTexture; + } + } + + for (int i = 0; i < FE_TERRAIN_MAX_LAYERS; i++) + { + if (Root["Layers"][i]["isAcive"].asBool()) + { + TERRAIN_SYSTEM.ActivateVacantLayerSlot(Entity, RESOURCE_MANAGER.GetMaterial(Root["Layers"][i]["Material ID"].asCString())); + TerrainComponent.GetLayerInSlot(i)->SetName(Root["Layers"][i]["Name"].asCString()); + } + } +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FETerrainSystem.h b/SubSystems/Scene/Components/Systems/FETerrainSystem.h new file mode 100644 index 0000000..87359e3 --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FETerrainSystem.h @@ -0,0 +1,112 @@ +#pragma once +#include "../../Scene/FESceneManager.h" + +namespace FocalEngine +{ +#define TERRAIN_SYSTEM_RESOURCE_TAG "TERRAIN_SYSTEM_PRIVATE_RESOURCE" + + class FOCAL_ENGINE_API FETerrainSystem + { + friend class FEScene; + friend class FERenderer; + friend class FEngine; + + SINGLETON_PRIVATE_PART(FETerrainSystem) + + static void OnMyComponentAdded(FEEntity* Entity); + static void OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing); + void RegisterOnComponentCallbacks(); + + static void DuplicateTerrainComponent(FEEntity* SourceEntity, FEEntity* TargetEntity); + + // ********************************** PointOnTerrain ********************************** + glm::dvec3 BinarySearch(FEEntity* TerrainEntity, int Count, float Start, float Finish, glm::dvec3 MouseRayStart, glm::dvec3 MouseRayDirection); + bool IntersectionInRange(FEEntity* TerrainEntity, float Start, float Finish, glm::dvec3 MouseRayStart, glm::dvec3 MouseRayDirection); + glm::dvec3 GetPointOnRay(FEEntity* TerrainEntity, glm::dvec3 MouseRayStart, glm::dvec3 MouseRayDirection, float Distance); + bool IsUnderGround(FEEntity* TerrainEntity, glm::dvec3 TestPoint); + // ********************************** PointOnTerrain END ********************************** + + // **************************** TERRAIN EDITOR TOOLS **************************** + std::string TerrainEntityIDWithBrushModeOn = ""; + + bool bBrushActive = false; + FE_TERRAIN_BRUSH_MODE BrushMode = FE_TERRAIN_BRUSH_NONE; + + size_t BrushLayerIndex = 0; + float BrushSize = 2.0f; + float BrushIntensity = 0.01f; + + FEShader* BrushOutputShader = nullptr; + FEShader* LayersNormalizeShader = nullptr; + FEShader* BrushVisualShader = nullptr; + FEMesh* PlaneMesh = nullptr; + + void UpdateBrush(glm::dvec3 MouseRayStart, glm::dvec3 MouseRayDirection); + size_t WaitBeforeUpdateMs = 50; + std::chrono::system_clock::time_point LastChangesTimeStamp; + void UpdateCPUHeightInfo(FEEntity* TerrainEntity); + void UpdateSnapedInstancedEntities(FEEntity* TerrainEntity); + + bool bCPUHeightInfoDirtyFlag = false; + size_t FramesBeforeUpdate = 50; + bool bBrushVisualFBCleared = false; + // **************************** TERRAIN EDITOR TOOLS END **************************** + + void InitTerrainEditTools(FEEntity* TerrainEntity); + bool UpdateLayerMapsRawData(FEEntity* TerrainEntity); + + static Json::Value TerrainComponentToJson(FEEntity* Entity); + static void TerrainComponentFromJson(FEEntity* Entity, Json::Value Root); + public: + SINGLETON_PUBLIC_PART(FETerrainSystem) + + FEAABB GetAABB(FEEntity* TerrainEntity); + FEAABB GetPureAABB(FEEntity* TerrainEntity); + + float GetHeightAt(FEEntity* TerrainEntity, glm::vec2 XZWorldPosition); + float GetLayerIntensityAt(FEEntity* TerrainEntity, glm::vec2 XZWorldPosition, int LayerIndex); + void ActivateVacantLayerSlot(FEEntity* TerrainEntity, FEMaterial* Material); + void FillTerrainLayerMask(FEEntity* TerrainEntity, const size_t LayerIndex); + void FillTerrainLayerMaskWithRawData(FEEntity* TerrainEntity, const unsigned char* RawData, const size_t LayerIndex); + void ClearTerrainLayerMask(FEEntity* TerrainEntity, size_t LayerIndex); + void DeleteTerrainLayerMask(FEEntity* TerrainEntity, size_t LayerIndex); + void LoadTerrainLayerMask(FEEntity* TerrainEntity, std::string FileName, const size_t LayerIndex); + void SaveTerrainLayerMask(FEEntity* TerrainEntity, std::string FileName, const size_t LayerIndex); + // ********************************** PointOnTerrain ********************************** + glm::dvec3 GetPointOnTerrain(FEEntity* TerrainEntity, glm::dvec3 MouseRayStart, glm::dvec3 MouseRayDirection, float StartDistance = 0.0f, float EndDistance = 256.0f); + // ********************************** PointOnTerrain END ********************************** + + void SnapInstancedEntity(FEEntity* TerrainEntity, FEEntity* EntityToSnap); + void UnSnapInstancedEntity(FEEntity* TerrainEntity, FEEntity* EntityToUnSnap); + + void ConnectInstancedEntityToLayer(FEEntity* TerrainEntity, FEEntity* EntityWithInstancedComponent, int LayerIndex); + void UnConnectInstancedEntityFromLayer(FEEntity* Entity); + + // **************************** TERRAIN EDITOR TOOLS **************************** + float GetBrushSize(); + void SetBrushSize(float NewValue); + + float GetBrushIntensity(); + void SetBrushIntensity(float NewValue); + + bool IsBrushActive(); + void SetBrushActive(bool NewValue); + + FE_TERRAIN_BRUSH_MODE GetBrushMode(); + void SetBrushMode(FEEntity* TerrainEntity, FE_TERRAIN_BRUSH_MODE NewValue); + + size_t GetBrushLayerIndex(); + void SetBrushLayerIndex(size_t NewValue); + // **************************** TERRAIN EDITOR TOOLS END **************************** + + void SetHeightMap(FETexture* HightMap, FEEntity* TerrainEntity); + void LoadHeightMap(std::string FileName, FEEntity* TerrainEntity); + }; + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetTerrainSystem(); + #define TERRAIN_SYSTEM (*static_cast(GetTerrainSystem())) +#else + #define TERRAIN_SYSTEM FETerrainSystem::GetInstance() +#endif +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FETransformSystem.cpp b/SubSystems/Scene/Components/Systems/FETransformSystem.cpp new file mode 100644 index 0000000..35d3ef5 --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FETransformSystem.cpp @@ -0,0 +1,152 @@ +#include "FETransformSystem.h" +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetTransformSystem() +{ + return FETransformSystem::GetInstancePointer(); +} +#endif + +FETransformSystem::FETransformSystem() +{ + RegisterOnComponentCallbacks(); + COMPONENTS_TOOL.RegisterComponentToJsonFunction(TransfromComponentToJson); + COMPONENTS_TOOL.RegisterComponentFromJsonFunction(TransfromComponentFromJson); + COMPONENTS_TOOL.RegisterComponentDuplicateFunction(DuplicateTransformComponent); +} + +void FETransformSystem::RegisterOnComponentCallbacks() +{ + SCENE_MANAGER.RegisterOnComponentConstructCallback(OnMyComponentAdded); + SCENE_MANAGER.RegisterOnComponentDestroyCallback(OnMyComponentDestroy); +} + +void FETransformSystem::OnMyComponentAdded(FEEntity* Entity) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FETransformComponent& TransformComponent = Entity->GetComponent(); + TransformComponent.ParentEntity = Entity; +} + +void FETransformSystem::OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FETransformComponent& TransformComponent = Entity->GetComponent(); +} + +FETransformSystem::~FETransformSystem() {}; + +void FETransformSystem::Update() +{ + std::vector ActiveScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active); + for (FEScene* Scene : ActiveScenes) + { + UpdateInternal(Scene->SceneGraph.GetRoot()); + } +} + +void FETransformSystem::DuplicateTransformComponent(FEEntity* SourceEntity, FEEntity* TargetEntity) +{ + if (SourceEntity == nullptr || TargetEntity == nullptr) + return; + + TargetEntity->GetComponent() = SourceEntity->GetComponent(); + TargetEntity->GetComponent().ParentEntity = TargetEntity; +} + +void FETransformSystem::UpdateInternal(FENaiveSceneGraphNode* SubTreeRoot) +{ + // If it is root node, then work only on children + if (SubTreeRoot->GetEntity() == nullptr) + { + auto Children = SubTreeRoot->GetChildren(); + for (size_t i = 0; i < Children.size(); i++) + { + UpdateInternal(Children[i]); + } + + return; + } + + FEScene* CurrentScene = SubTreeRoot->GetEntity()->GetParentScene(); + + // If this is root node or parent is root node, then world space matrix is equal to local space matrix. + FETransformComponent& CurrentTransform = SubTreeRoot->GetEntity()->GetComponent(); + if (SubTreeRoot->GetParent() == nullptr || SubTreeRoot->GetParent() == CurrentScene->SceneGraph.GetRoot()) + { + CurrentTransform.Update(); + CurrentTransform.WorldSpaceMatrix = CurrentTransform.LocalSpaceMatrix; + } + + // FIXME: Instanced position should update when parent (or terrain) is updated, currently no update occurs. + //bool bWasDirty = CurrentTransform.IsDirty(); + //CurrentTransform.SetDirtyFlag(false); + auto Children = SubTreeRoot->GetChildren(); + for (size_t i = 0; i < Children.size(); i++) + { + FETransformComponent& ChildTransform = Children[i]->GetEntity()->GetComponent(); + + ChildTransform.Update(); + ChildTransform.WorldSpaceMatrix = CurrentTransform.WorldSpaceMatrix * ChildTransform.LocalSpaceMatrix; + //if (CurrentTransform.IsDirty()) + // ChildTransform.SetDirtyFlag(true); + UpdateInternal(Children[i]); + } +} + +Json::Value FETransformSystem::TransfromComponentToJson(FEEntity* Entity) +{ + Json::Value Root; + FETransformComponent& TransformComponent = Entity->GetComponent(); + + glm::vec3 Position = TransformComponent.GetPosition(); + Root["Position"]["X"] = Position.x; + Root["Position"]["Y"] = Position.y; + Root["Position"]["Z"] = Position.z; + + glm::quat Rotation = TransformComponent.GetQuaternion(); + Root["Rotation"]["X"] = Rotation.x; + Root["Rotation"]["Y"] = Rotation.y; + Root["Rotation"]["Z"] = Rotation.z; + Root["Rotation"]["W"] = Rotation.w; + + Root["Scale"]["UniformScaling"] = TransformComponent.IsUniformScalingSet(); + glm::vec3 Scale = TransformComponent.GetScale(); + Root["Scale"]["X"] = Scale.x; + Root["Scale"]["Y"] = Scale.y; + Root["Scale"]["Z"] = Scale.z; + + return Root; +} + +void FETransformSystem::TransfromComponentFromJson(FEEntity* Entity, Json::Value Root) +{ + FETransformComponent& TransformComponent = Entity->GetComponent(); + + glm::vec3 Position; + Position.x = Root["Position"]["X"].asFloat(); + Position.y = Root["Position"]["Y"].asFloat(); + Position.z = Root["Position"]["Z"].asFloat(); + TransformComponent.SetPosition(Position); + + glm::quat Rotation; + Rotation.x = Root["Rotation"]["X"].asFloat(); + Rotation.y = Root["Rotation"]["Y"].asFloat(); + Rotation.z = Root["Rotation"]["Z"].asFloat(); + Rotation.w = Root["Rotation"]["W"].asFloat(); + TransformComponent.SetQuaternion(Rotation); + + bool bUniformScaling = Root["Scale"]["UniformScaling"].asBool(); + TransformComponent.SetUniformScaling(bUniformScaling); + + glm::vec3 Scale; + Scale.x = Root["Scale"]["X"].asFloat(); + Scale.y = Root["Scale"]["Y"].asFloat(); + Scale.z = Root["Scale"]["Z"].asFloat(); + TransformComponent.SetScale(Scale); +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FETransformSystem.h b/SubSystems/Scene/Components/Systems/FETransformSystem.h new file mode 100644 index 0000000..1b60648 --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FETransformSystem.h @@ -0,0 +1,36 @@ +#pragma once +#include "../../Scene/FESceneManager.h" + +namespace FocalEngine +{ + class FOCAL_ENGINE_API FETransformSystem + { + friend class FEScene; + friend class FESceneManager; + friend class FERenderer; + friend class FEngine; + + SINGLETON_PRIVATE_PART(FETransformSystem) + + static void OnMyComponentAdded(FEEntity* Entity); + static void OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing); + void RegisterOnComponentCallbacks(); + + void Update(); + void UpdateInternal(FENaiveSceneGraphNode* SubTreeRoot); + + static Json::Value TransfromComponentToJson(FEEntity* Entity); + static void TransfromComponentFromJson(FEEntity* Entity, Json::Value Root); + static void DuplicateTransformComponent(FEEntity* SourceEntity, FEEntity* TargetEntity); + public: + SINGLETON_PUBLIC_PART(FETransformSystem) + + }; + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetTransformSystem(); + #define TRANSFORM_SYSTEM (*static_cast(GetTransformSystem())) +#else + #define TRANSFORM_SYSTEM FETransformSystem::GetInstance() +#endif +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FEVirtualUISystem.cpp b/SubSystems/Scene/Components/Systems/FEVirtualUISystem.cpp new file mode 100644 index 0000000..287c963 --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FEVirtualUISystem.cpp @@ -0,0 +1,252 @@ +#include "FEVirtualUISystem.h" +#include "../../Renderer/FERenderer.h" +#include "../../../FEngine.h" +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetVirtualUISystem() +{ + return FEVirtualUISystem::GetInstancePointer(); +} +#endif + +FEVirtualUISystem::FEVirtualUISystem() +{ + CanvasMaterial = RESOURCE_MANAGER.CreateMaterial(); + CanvasMaterial->SetName("VirtualUISystem_CanvasMaterial"); + RESOURCE_MANAGER.SetTagIternal(CanvasMaterial, ENGINE_RESOURCE_TAG); + CanvasMaterial->Shader = RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/); + + DummyGameModel = RESOURCE_MANAGER.CreateGameModel(); + DummyGameModel->SetName("VirtualUISystem_DummyGameModel"); + DummyGameModel->SetMaterial(CanvasMaterial); + RESOURCE_MANAGER.SetTagIternal(DummyGameModel, ENGINE_RESOURCE_TAG); + DummyGameModelComponent.SetGameModel(DummyGameModel); + + RegisterOnComponentCallbacks(); + COMPONENTS_TOOL.RegisterComponentToJsonFunction(VirtualUIComponentToJson); + COMPONENTS_TOOL.RegisterComponentFromJsonFunction(VirtualUIComponentFromJson); + COMPONENTS_TOOL.RegisterComponentDuplicateFunction(DuplicateVirtualUIComponent); +} + +void FEVirtualUISystem::RegisterOnComponentCallbacks() +{ + SCENE_MANAGER.RegisterOnComponentConstructCallback(OnMyComponentAdded); + SCENE_MANAGER.RegisterOnComponentDestroyCallback(OnMyComponentDestroy); +} + +void FEVirtualUISystem::OnMyComponentAdded(FEEntity* Entity) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FEVirtualUIComponent& VirtualUIComponent = Entity->GetComponent(); + VirtualUIComponent.ParentEntity = Entity; + VirtualUIComponent.SetRenderFunction(DummyRenderFunction); +} + +void FEVirtualUISystem::DuplicateVirtualUIComponent(FEEntity* SourceEntity, FEEntity* TargetEntity) +{ + if (SourceEntity == nullptr || TargetEntity == nullptr || !SourceEntity->HasComponent()) + return; + + FEVirtualUIComponent& VirtualUIComponent = SourceEntity->GetComponent(); + TargetEntity->AddComponent(); + FEVirtualUIComponent& NewVirtualUIComponent = TargetEntity->GetComponent(); + + NewVirtualUIComponent.SetWindowToListen(VirtualUIComponent.GetWindowToListen()); + NewVirtualUIComponent.SetRenderFunction(VirtualUIComponent.GetRenderFunction()); + + NewVirtualUIComponent.SetMouseButtonPassThrough(VirtualUIComponent.bMouseButtonPassThrough); + NewVirtualUIComponent.SetMouseMovePassThrough(VirtualUIComponent.bMouseMovePassThrough); + NewVirtualUIComponent.SetCharPassThrough(VirtualUIComponent.bCharPassThrough); + NewVirtualUIComponent.SetKeyPassThrough(VirtualUIComponent.bKeyPassThrough); + NewVirtualUIComponent.SetDropPassThrough(VirtualUIComponent.bDropPassThrough); + NewVirtualUIComponent.SetScrollPassThrough(VirtualUIComponent.bScrollPassThrough); + + NewVirtualUIComponent.SetVisibility(VirtualUIComponent.IsVisible()); + NewVirtualUIComponent.SetInputActive(VirtualUIComponent.IsInputActive()); + + NewVirtualUIComponent.InvokeResize(VirtualUIComponent.GetWidth(), VirtualUIComponent.GetHeight()); +} + +void FEVirtualUISystem::OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing) +{ + if (Entity == nullptr || !Entity->HasComponent()) + return; + + FEVirtualUIComponent& VirtualUIComponent = Entity->GetComponent(); +} + +FEVirtualUISystem::~FEVirtualUISystem() {}; + +Json::Value FEVirtualUISystem::VirtualUIComponentToJson(FEEntity* Entity) +{ + Json::Value Root; + if (Entity == nullptr || !Entity->HasComponent()) + { + LOG.Add("FEVirtualUISystem::VirtualUIComponentToJson Entity is nullptr or does not have FEVirtualUIComponent", "FE_LOG_ECS", FE_LOG_WARNING); + return Root; + } + FEVirtualUIComponent& VirtualUIComponent = Entity->GetComponent(); + + // TO-DO: Save window to listen to? + //Root["Window to listen"] = VirtualUIComponent.GetWindowToListen() != nullptr; + + Root["Canvas mesh ID"] = VirtualUIComponent.CanvasMesh->GetObjectID(); + Root["Internal resolution"]["Width"] = static_cast(VirtualUIComponent.GetCanvasResolution().x); + Root["Internal resolution"]["Height"] = static_cast(VirtualUIComponent.GetCanvasResolution().y); + + Root["Visibility"] = VirtualUIComponent.IsVisible(); + Root["Input Active"] = VirtualUIComponent.IsInputActive(); + + // TO-DO: How to save function pointers? + //Root["Render Function"] = VirtualUIComponent.GetRenderFunction() != nullptr; + + Root["Mouse Button PassThrough"] = VirtualUIComponent.IsMouseButtonPassThroughActive(); + Root["Mouse Move PassThrough"] = VirtualUIComponent.IsMouseMovePassThroughActive(); + Root["Scroll PassThrough"] = VirtualUIComponent.IsScrollPassThroughActive(); + Root["Char PassThrough"] = VirtualUIComponent.IsCharPassThroughActive(); + Root["Key PassThrough"] = VirtualUIComponent.IsKeyPassThroughActive(); + Root["Drop PassThrough"] = VirtualUIComponent.IsDropPassThroughActive(); + + return Root; +} + +void FEVirtualUISystem::VirtualUIComponentFromJson(FEEntity* Entity, Json::Value Root) +{ + if (Entity == nullptr) + { + LOG.Add("FEVirtualUISystem::VirtualUIComponentFromJson Entity is nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + glm::ivec2 Resolution = glm::vec2(Root["Internal resolution"]["Width"].asInt(), Root["Internal resolution"]["Height"].asInt()); + FEMesh* CanvasMesh = RESOURCE_MANAGER.GetMesh(Root["Canvas mesh ID"].asString()); + Entity->AddComponent(Resolution.x, Resolution.y, CanvasMesh); + FEVirtualUIComponent& VirtualUIComponent = Entity->GetComponent(); + + //VirtualUIComponent.SetRenderFunction(TestRender); + VirtualUIComponent.SetWindowToListen(APPLICATION.GetMainWindow()); + + VirtualUIComponent.SetCanvasResolution(Resolution); + + VirtualUIComponent.SetMouseButtonPassThrough(Root["Mouse Button PassThrough"].asBool()); + VirtualUIComponent.SetMouseMovePassThrough(Root["Mouse Move PassThrough"].asBool()); + VirtualUIComponent.SetScrollPassThrough(Root["Scroll PassThrough"].asBool()); + VirtualUIComponent.SetCharPassThrough(Root["Char PassThrough"].asBool()); + VirtualUIComponent.SetKeyPassThrough(Root["Key PassThrough"].asBool()); + VirtualUIComponent.SetDropPassThrough(Root["Drop PassThrough"].asBool()); +} + +void FEVirtualUISystem::RenderVirtualUIComponent(FEEntity* Entity) +{ + if (Entity == nullptr || !Entity->HasComponent()) + { + LOG.Add("FEVirtualUISystem::RenderVirtualUIComponent Entity is nullptr or does not have FEVirtualUIComponent", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + FEVirtualUIComponent& VirtualUIComponent = Entity->GetComponent(); + + RenderVirtualUIComponent(Entity, CanvasMaterial); + CanvasMaterial->ClearAllTexturesInfo(); +} + +void FEVirtualUISystem::RenderVirtualUIComponent(FEEntity* Entity, FEMaterial* ForceMaterial) +{ + if (Entity == nullptr || !Entity->HasComponent()) + { + LOG.Add("FEVirtualUISystem::RenderVirtualUIComponent Entity is nullptr or does not have FEVirtualUIComponent", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + + if (ForceMaterial == nullptr) + { + LOG.Add("FEVirtualUISystem::RenderVirtualUIComponent ForceMaterial is nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + FEVirtualUIComponent& VirtualUIComponent = Entity->GetComponent(); + + ForceMaterial->SetAlbedoMap(VirtualUIComponent.Framebuffer->GetColorAttachment()); + DummyGameModel->SetMaterial(ForceMaterial); + DummyGameModel->SetMesh(VirtualUIComponent.CanvasMesh); + + RENDERER.RenderGameModelComponent(DummyGameModelComponent, Entity->GetComponent(), Entity->GetParentScene(), nullptr, true); + + ForceMaterial->SetAlbedoMap(nullptr); + DummyGameModel->SetMaterial(nullptr); + DummyGameModel->SetMesh(nullptr); + CanvasMaterial->SetAlbedoMap(nullptr); +} + +void FEVirtualUISystem::Update() +{ + std::vector ActiveScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active | FESceneFlag::Renderable); + for (size_t i = 0; i < ActiveScenes.size(); i++) + { + FEEntity* CameraEntity = CAMERA_SYSTEM.GetMainCamera(ActiveScenes[i]); + if (CameraEntity == nullptr) + continue; + + FECameraComponent& CameraComponent = CameraEntity->GetComponent(); + + FEViewport* CurrentViewport = CAMERA_SYSTEM.GetMainCameraViewport(ActiveScenes[i]); + if (CurrentViewport == nullptr) + continue; + + glm::ivec2 ViewportPosition = glm::ivec2(CurrentViewport->GetX(), CurrentViewport->GetY()); + glm::ivec2 ViewportSize = glm::ivec2(CurrentViewport->GetWidth(), CurrentViewport->GetHeight()); + + glm::dvec3 MouseRay = GEOMETRY.CreateMouseRayToWorld(INPUT.GetMouseX(), INPUT.GetMouseY(), + CameraComponent.GetViewMatrix(), CameraComponent.GetProjectionMatrix(), + ViewportPosition, ViewportSize); + + std::vector Entities = ActiveScenes[i]->GetEntityListWithComponent(); + for (size_t j = 0; j < Entities.size(); j++) + { + FEVirtualUIComponent& VirtualUIComponent = Entities[j]->GetComponent(); + + if (VirtualUIComponent.bMouseMovePassThrough) + VirtualUIComponent.UpdateInteractionRay(CameraEntity->GetComponent().GetPosition(FE_WORLD_SPACE), MouseRay); + } + + } +} + +FEEntity* FEVirtualUISystem::GetParentEntity(FEVirtualUI* VirtualUI) +{ + std::vector ActiveScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active | FESceneFlag::Renderable); + for (size_t i = 0; i < ActiveScenes.size(); i++) + { + std::vector Entities = ActiveScenes[i]->GetEntityListWithComponent(); + for (size_t j = 0; j < Entities.size(); j++) + { + FEVirtualUIComponent& VirtualUIComponent = Entities[j]->GetComponent(); + + if (VirtualUIComponent.VirtualUI == VirtualUI) + return Entities[j]; + } + } + + return nullptr; +} + +void FEVirtualUISystem::DummyRenderFunction(FEVirtualUI* VirtualUI) +{ + FEEntity* ParentEntity = VIRTUAL_UI_SYSTEM.GetParentEntity(VirtualUI); + if (ParentEntity == nullptr) + return; + + FEVirtualUIComponent& VirtualUIComponent = ParentEntity->GetComponent(); + ImGui::SetNextWindowSize(ImVec2(static_cast(VirtualUIComponent.GetWidth()), static_cast(VirtualUIComponent.GetHeight()))); + if (ImGui::Begin(std::string("Dummy UI##" + ParentEntity->GetObjectID()).c_str(), nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoNav)) + { + ImVec2 Center = ImGui::GetWindowContentRegionMax() / 2.0f; + + ImGui::SetWindowFontScale(10.0f); + ImVec2 TextSize = ImGui::CalcTextSize("Dummy UI"); + ImGui::SetCursorPos(Center - TextSize / 2.0f); + ImGui::Text("Dummy UI"); + + ImGui::End(); + } +} \ No newline at end of file diff --git a/SubSystems/Scene/Components/Systems/FEVirtualUISystem.h b/SubSystems/Scene/Components/Systems/FEVirtualUISystem.h new file mode 100644 index 0000000..36b11af --- /dev/null +++ b/SubSystems/Scene/Components/Systems/FEVirtualUISystem.h @@ -0,0 +1,44 @@ +#pragma once +#include "../../Scene/FESceneManager.h" + +namespace FocalEngine +{ + class FOCAL_ENGINE_API FEVirtualUISystem + { + friend class FEScene; + friend class FERenderer; + friend class FEngine; + + SINGLETON_PRIVATE_PART(FEVirtualUISystem) + + FEMaterial* CanvasMaterial = nullptr; + FEGameModel* DummyGameModel = nullptr; + FEGameModelComponent DummyGameModelComponent; + + static void OnMyComponentAdded(FEEntity* Entity); + static void OnMyComponentDestroy(FEEntity* Entity, bool bIsSceneClearing); + void RegisterOnComponentCallbacks(); + + static void DuplicateVirtualUIComponent(FEEntity* SourceEntity, FEEntity* TargetEntity); + + static Json::Value VirtualUIComponentToJson(FEEntity* Entity); + static void VirtualUIComponentFromJson(FEEntity* Entity, Json::Value Root); + + void Update(); + + static void DummyRenderFunction(FEVirtualUI* VirtualUI); + public: + SINGLETON_PUBLIC_PART(FEVirtualUISystem) + + void RenderVirtualUIComponent(FEEntity* Entity); + void RenderVirtualUIComponent(FEEntity* Entity, FEMaterial* ForceMaterial); + FEEntity* GetParentEntity(FEVirtualUI* VirtualUI); + }; + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetVirtualUISystem(); + #define VIRTUAL_UI_SYSTEM (*static_cast(GetVirtualUISystem())) +#else + #define VIRTUAL_UI_SYSTEM FEVirtualUISystem::GetInstance() +#endif +} \ No newline at end of file diff --git a/SubSystems/Scene/FEEntity.cpp b/SubSystems/Scene/FEEntity.cpp new file mode 100644 index 0000000..5602ef5 --- /dev/null +++ b/SubSystems/Scene/FEEntity.cpp @@ -0,0 +1,110 @@ +#include "FEEntity.h" +using namespace FocalEngine; +#include "FEScene.h" + +FEEntity::FEEntity(entt::entity AssignedEnTTEntity, FEScene* Scene) : FEObject(FE_OBJECT_TYPE::FE_ENTITY, "Unnamed Entity") +{ + EnTTEntity = AssignedEnTTEntity; + ParentScene = Scene; +} + +entt::registry& FEEntity::GetRegistry() +{ + return ParentScene->Registry; +} + +FEEntity::~FEEntity() +{ + ParentScene->Registry.destroy(EnTTEntity); + ParentScene->ClearEntityRecords(GetObjectID(), EnTTEntity); +} + +Json::Value FEEntity::ToJson() +{ + Json::Value Root; + Root["FEObjectData"] = RESOURCE_MANAGER.SaveFEObjectPart(this); + SaveComponents(Root["Components"]); + + return Root; +} + +void FEEntity::SaveComponents(Json::Value& Root) +{ + std::vector List = GetComponentsInfoList(); + for (size_t i = 0; i < List.size(); i++) + { + if (List[i].ToJson != nullptr) + Root[List[i].Name] = List[i].ToJson(this); + } +} + +void FEEntity::FromJson(Json::Value Root) +{ + FEObjectLoadedData Data = RESOURCE_MANAGER.LoadFEObjectPart(Root["FEObjectData"]); + + // ID and Name should be set before calling this function + if (Data.ID != GetObjectID()) + { + LOG.Add("FEEntity::FromJson: ID mismatch!", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + + if (Data.Name != GetName()) + { + LOG.Add("FEEntity::FromJson: Name mismatch!", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + + Tag = Data.Tag; + Type = Data.Type; + + // Load components. + std::vector ComponentsList = Root["Components"].getMemberNames(); + COMPONENTS_TOOL.SortComponentsByLoadingPriority(ComponentsList); + + for (size_t i = 0; i < ComponentsList.size(); i++) + { + std::string ComponentName = ComponentsList[i]; + FEComponentTypeInfo* ComponentInfo = COMPONENTS_TOOL.GetComponentInfoByName(ComponentName); + + if (ComponentInfo == nullptr) + { + LOG.Add("FEEntity::FromJson: Could not find component info for component: " + ComponentName, "FE_LOG_LOADING", FE_LOG_ERROR); + continue; + } + + if (ComponentInfo->FromJson == nullptr) + { + LOG.Add("FEEntity::FromJson: Component: " + ComponentName + " does not have FromJson function!", "FE_LOG_LOADING", FE_LOG_ERROR); + continue; + } + + ComponentInfo->FromJson(this, Root["Components"][ComponentName]); + } +} + +std::vector FEEntity::GetComponentsInfoList() +{ + std::vector Result; + + // Loop through all components types + for (auto&& CurrentComponent : GetRegistry().storage()) + { + entt::id_type ComponentID = CurrentComponent.first; + // Add only components that current entity has + if (auto& Storage = CurrentComponent.second; Storage.contains(EnTTEntity)) + { + if (COMPONENTS_TOOL.ComponentIDToInfo.find(ComponentID) != COMPONENTS_TOOL.ComponentIDToInfo.end()) + { + Result.push_back(COMPONENTS_TOOL.ComponentIDToInfo[ComponentID]); + } + } + } + + return Result; +} + +FEScene* FEEntity::GetParentScene() +{ + return ParentScene; +} \ No newline at end of file diff --git a/SubSystems/Scene/FEEntity.h b/SubSystems/Scene/FEEntity.h new file mode 100644 index 0000000..50bb2e0 --- /dev/null +++ b/SubSystems/Scene/FEEntity.h @@ -0,0 +1,88 @@ +#pragma once + +#include "../Renderer/FEFramebuffer.h" +#include "FEPrefab.h" +#include "Components/FEComponents.h" + +namespace FocalEngine +{ + class FEScene; + class FOCAL_ENGINE_API FEEntity : public FEObject + { + friend class FERenderer; + friend class FEResourceManager; + friend class FEScene; + friend class FENaiveSceneGraphNode; + friend class FENaiveSceneGraph; + + FEEntity(entt::entity AssignedEnTTEntity, FEScene* Scene); + ~FEEntity(); + + entt::entity EnTTEntity = entt::null; + FEScene* ParentScene = nullptr; + + entt::registry& GetRegistry(); + + void FromJson(Json::Value Root); + void SaveComponents(Json::Value& Root); + public: + template + bool AddComponent(Args&&... args) + { + if (HasComponent()) + { + LOG.Add("Component already exists in entity!", "FE_LOG_ECS", FE_LOG_WARNING); + return false; + } + + if (COMPONENTS_TOOL.ComponentIDToInfo.find(entt::type_id().hash()) == COMPONENTS_TOOL.ComponentIDToInfo.end()) + return false; + + std::string ErrorMessage; + if (!COMPONENTS_TOOL.ComponentIDToInfo[entt::type_id().hash()].CanBeAddedToEntity(this, &ErrorMessage)) + { + LOG.Add("Can not add component: " + ErrorMessage, "FE_LOG_ECS", FE_LOG_ERROR); + return false; + } + + GetRegistry().emplace(EnTTEntity, std::forward(args)...); + return true; + } + + template + T& GetComponent() + { + if (!HasComponent()) + { + LOG.Add("Component does not exist in entity!", "FE_LOG_ECS", FE_LOG_ERROR); + // FIXME: Should return nullptr or similar instead of throwing an exception. + throw std::runtime_error("Component does not exist in entity"); + } + + return GetRegistry().get(EnTTEntity); + } + + template + bool HasComponent() + { + return GetRegistry().all_of(EnTTEntity); + } + + template + void RemoveComponent() + { + if (!HasComponent()) + { + LOG.Add("Component does not exist in entity!", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } + + GetRegistry().remove(EnTTEntity); + } + + std::vector GetComponentsInfoList(); + FEScene* GetParentScene(); + + Json::Value ToJson(); + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/FENaiveSceneGraph.cpp b/SubSystems/Scene/FENaiveSceneGraph.cpp new file mode 100644 index 0000000..9f40bad --- /dev/null +++ b/SubSystems/Scene/FENaiveSceneGraph.cpp @@ -0,0 +1,381 @@ +#include "FENaiveSceneGraph.h" +using namespace FocalEngine; + +#include "FEScene.h" + +FENaiveSceneGraph::FENaiveSceneGraph() +{ + +} + +void FENaiveSceneGraph::Clear() +{ + bClearing = true; + DeleteNode(Root); + Initialize(ParentScene); + bClearing = false; +} + +void FENaiveSceneGraph::Initialize(FEScene* Scene) +{ + ParentScene = Scene; + FENaiveSceneGraphNode* NewRoot = new FENaiveSceneGraphNode("SceneRoot"); + Root = NewRoot; +} + +FENaiveSceneGraph::~FENaiveSceneGraph() +{ + delete Root; +} + +FENaiveSceneGraphNode* FENaiveSceneGraph::GetRoot() const +{ + return Root; +} + +FENaiveSceneGraphNode* FENaiveSceneGraph::GetNode(std::string ID) +{ + if (ID == Root->GetObjectID()) + return Root; + + return Root->GetChild(ID); +} + +FENaiveSceneGraphNode* FENaiveSceneGraph::GetNodeByEntityID(std::string EntityID) +{ + return Root->GetChildByEntityID(EntityID); +} + +std::string FENaiveSceneGraph::AddNode(FEEntity* Entity, bool bPreserveWorldTransform) +{ + FENaiveSceneGraphNode* NewNode = nullptr; + NewNode = GetNodeByEntityID(Entity->GetObjectID()); + if (NewNode != nullptr) + { + LOG.Add("Entity already exists in the scene graph", "FE_LOG_SCENE", FE_LOG_WARNING); + // Entity already exists in the scene graph + return NewNode->GetObjectID(); + } + + NewNode = new FENaiveSceneGraphNode(Entity->GetName()); + NewNode->Entity = Entity; + Root->AddChild(NewNode, bPreserveWorldTransform); + + return NewNode->GetObjectID(); +} + +bool FENaiveSceneGraph::MoveNode(std::string NodeID, std::string NewParentID, bool bPreserveWorldTransform) +{ + FENaiveSceneGraphNode* NodeToMove = GetNode(NodeID); + FENaiveSceneGraphNode* NewParent = GetNode(NewParentID); + + if (NodeToMove == nullptr || NewParent == nullptr) + return false; + + if (NodeToMove->GetParent() == NewParent) + return false; + + if (IsDescendant(NodeToMove, NewParent)) + return false; + + FENaiveSceneGraphNode* OldParent = NodeToMove->GetParent(); + + // Temporarily detach the node. + // With temporary detach, bPreserveWorldTransform could be a problem. + DetachNode(NodeToMove, bPreserveWorldTransform); + AddNodeInternal(NewParent, NodeToMove, bPreserveWorldTransform); + + // Check for cycles + if (HasCycle(GetRoot())) + { + // If a cycle is created, revert the change + DetachNode(NodeToMove, bPreserveWorldTransform); + AddNodeInternal(OldParent, NodeToMove, bPreserveWorldTransform); + return false; + } + + return true; +} + +FENaiveSceneGraphNode* FENaiveSceneGraph::DuplicateNode(std::string NodeIDToDuplicate, std::string NewParentID, bool bAddCopyInName) +{ + FENaiveSceneGraphNode* NodeToDuplicate = GetNode(NodeIDToDuplicate); + FENaiveSceneGraphNode* NewParent = GetNode(NewParentID); + + if (NodeToDuplicate == nullptr || NewParent == nullptr) + return nullptr; + + return DuplicateNode(NodeToDuplicate, NewParent, bAddCopyInName); +} + +FENaiveSceneGraphNode* FENaiveSceneGraph::DuplicateNode(FENaiveSceneGraphNode* NodeToDuplicate, FENaiveSceneGraphNode* NewParent, bool bAddCopyInName) +{ + if (NodeToDuplicate == nullptr || NewParent == nullptr) + return nullptr; + + FENaiveSceneGraphNode* OriginalParent = NodeToDuplicate->GetParent(); + + // Try to duplicate the node. + bool bDuplicationSuccess = true; + FENaiveSceneGraphNode* TopMostDuplicate = new FENaiveSceneGraphNode(NodeToDuplicate->GetName() + (bAddCopyInName ? "_Copy" : "")); + TopMostDuplicate->Entity = ParentScene->DuplicateEntity(NodeToDuplicate->Entity, bAddCopyInName ? "" : NodeToDuplicate->Entity->GetName()); + NewParent->AddChild(TopMostDuplicate); + + for (size_t i = 0; i < NodeToDuplicate->Children.size(); i++) + { + if (!DuplicateNodeInternal(TopMostDuplicate, NodeToDuplicate->GetChildren()[i], bAddCopyInName)) + { + bDuplicationSuccess = false; + break; + } + } + + if (!bDuplicationSuccess) + { + DeleteNode(TopMostDuplicate); + return nullptr; + } + + // Check for cycles + if (HasCycle(GetRoot())) + { + // If a cycle is created, delete duplicated node + DeleteNode(TopMostDuplicate); + return nullptr; + } + + return TopMostDuplicate; +} + +bool FENaiveSceneGraph::DuplicateNodeInternal(FENaiveSceneGraphNode* Parent, FENaiveSceneGraphNode* NodeToDuplicate, bool bAddCopyInName) +{ + FENaiveSceneGraphNode* Duplicate = new FENaiveSceneGraphNode(NodeToDuplicate->GetName() + (bAddCopyInName ? "_Copy" : "")); + Duplicate->Entity = ParentScene->DuplicateEntity(NodeToDuplicate->Entity); + Parent->AddChild(Duplicate); + + for (size_t i = 0; i < NodeToDuplicate->Children.size(); i++) + { + if (!DuplicateNodeInternal(Duplicate, NodeToDuplicate->GetChildren()[i], bAddCopyInName)) + return false; + } + + return true; +} + +void FENaiveSceneGraph::AddNodeInternal(FENaiveSceneGraphNode* NodeToAdd, bool bPreserveWorldTransform) +{ + Root->AddChild(NodeToAdd, bPreserveWorldTransform); +} + +void FENaiveSceneGraph::AddNodeInternal(FENaiveSceneGraphNode* Parent, FENaiveSceneGraphNode* NodeToAdd, bool bPreserveWorldTransform) +{ + Parent->AddChild(NodeToAdd, bPreserveWorldTransform); +} + +FENaiveSceneGraphNode* FENaiveSceneGraph::ImportNode(FENaiveSceneGraphNode* NodeFromDifferentSceneGraph, FENaiveSceneGraphNode* TargetParent, std::function Filter) +{ + FENaiveSceneGraphNode* Result = nullptr; + if (NodeFromDifferentSceneGraph == nullptr) + { + LOG.Add("NodeFromDifferentSceneGraph is nullptr in FENaiveSceneGraph::ImportEntity", "FE_LOG_ECS", FE_LOG_ERROR); + return Result; + } + + FEEntity* EntityFromDifferentScene = NodeFromDifferentSceneGraph->Entity; + if (EntityFromDifferentScene->GetParentScene() == ParentScene) + { + LOG.Add("EntityFromDifferentScene is already in this scene in FENaiveSceneGraph::ImportEntity", "FE_LOG_ECS", FE_LOG_WARNING); + return Result; + } + + if (Filter != nullptr && !Filter(EntityFromDifferentScene)) + return Result; + + if (TargetParent == nullptr) + TargetParent = GetRoot(); + + Result = DuplicateNode(NodeFromDifferentSceneGraph, TargetParent, false); + + return Result; +} + +void FENaiveSceneGraph::DeleteNode(FENaiveSceneGraphNode* NodeToDelete) +{ + if (NodeToDelete == nullptr) + return; + + if (NodeToDelete == Root && !bClearing) + return; + + DetachNode(NodeToDelete); + delete NodeToDelete; +} + +void FENaiveSceneGraph::DetachNode(FENaiveSceneGraphNode* NodeToDetach, bool bPreserveWorldTransform) +{ + if (NodeToDetach == Root && !bClearing) + return; + + Root->DetachChild(NodeToDetach, bPreserveWorldTransform); +} + +std::vector FENaiveSceneGraph::GetNodeByName(std::string Name) +{ + std::vector Entities; + std::vector Children = Root->GetChildren(); + + for (size_t i = 0; i < Children.size(); i++) + { + if (Children[i]->GetName() == Name) + Entities.push_back(Children[i]); + } + + return Entities; +} + +bool FENaiveSceneGraph::IsDescendant(FENaiveSceneGraphNode* PotentialAncestor, FENaiveSceneGraphNode* PotentialDescendant) +{ + if (PotentialDescendant == nullptr) + return false; + + if (PotentialAncestor == PotentialDescendant) + return true; + + return IsDescendant(PotentialAncestor, PotentialDescendant->GetParent()); +} + +bool FENaiveSceneGraph::HasCycle(FENaiveSceneGraphNode* NodeToCheck) +{ + std::unordered_set Visited; + std::unordered_set RecursionStack; + return HasCycleInternal(NodeToCheck, Visited, RecursionStack); +} + +bool FENaiveSceneGraph::HasCycleInternal(FENaiveSceneGraphNode* NodeToCheck, + std::unordered_set& Visited, + std::unordered_set& RecursionStack) +{ + if (NodeToCheck == nullptr) + return false; + + // If the node is already in the recursion stack, we've found a cycle + if (RecursionStack.find(NodeToCheck) != RecursionStack.end()) + return true; + + // If we've already visited this node and found no cycles, we can return false + if (Visited.find(NodeToCheck) != Visited.end()) + return false; + + // Mark the current node as visited and part of recursion stack + Visited.insert(NodeToCheck); + RecursionStack.insert(NodeToCheck); + + // Recur for all the children + for (auto& child : NodeToCheck->GetChildren()) + { + if (HasCycleInternal(child, Visited, RecursionStack)) + return true; + } + + // Remove the node from recursion stack + RecursionStack.erase(NodeToCheck); + + return false; +} + +size_t FENaiveSceneGraph::GetNodeCount() +{ + return Root->GetRecursiveChildCount(); +} + +std::vector FENaiveSceneGraph::GetAllNodes() +{ + return Root->GetAllNodesInternal(); +} + +Json::Value FENaiveSceneGraph::ToJson(std::function Filter) +{ + Json::Value Root; + std::vector AllNodes = GetAllNodes(); + // Remove root node. It is not needed in serialization + AllNodes.erase(AllNodes.begin()); + + for (size_t i = 0; i < AllNodes.size(); i++) + { + if (Filter != nullptr && !Filter(AllNodes[i]->GetEntity())) + continue; + + Root["Nodes"][AllNodes[i]->GetObjectID()] = AllNodes[i]->ToJson(Filter); + } + + return Root; +} + +void FENaiveSceneGraph::FromJson(Json::Value Root) +{ + Clear(); + + Json::Value Nodes = Root["Nodes"]; + std::unordered_map LoadedNodes; + + // First pass: Create all nodes + for (Json::Value::const_iterator NodeIterator = Nodes.begin(); NodeIterator != Nodes.end(); NodeIterator++) + { + std::string NodeID = NodeIterator.key().asString(); + Json::Value NodeData = *NodeIterator; + + FENaiveSceneGraphNode* NewNode = new FENaiveSceneGraphNode(NodeData["Name"].asString()); + + FEEntity* NewEntity = nullptr; + FEObjectLoadedData LoadedObjectData = RESOURCE_MANAGER.LoadFEObjectPart(NodeData["Entity"]["FEObjectData"]); + // Before passing data to node, we need to create entity + NewEntity = ParentScene->CreateEntityOrphan(LoadedObjectData.Name, LoadedObjectData.ID); + + NewNode->Entity = NewEntity; + NewNode->FromJson(NodeData); + LoadedNodes[NodeID] = NewNode; + } + + // Second pass: Set up parent-child relationships + for (Json::Value::const_iterator NodeIterator = Nodes.begin(); NodeIterator != Nodes.end(); NodeIterator++) + { + std::string NodeID = NodeIterator.key().asString(); + Json::Value NodeData = *NodeIterator; + + FENaiveSceneGraphNode* CurrentNode = LoadedNodes[NodeID]; + std::string ParentID = NodeData["ParentID"].asString(); + + FENaiveSceneGraphNode* ParentNode = LoadedNodes[ParentID]; + // If we can not find parent, it is root + if (ParentNode == nullptr) + { + this->Root->AddChild(CurrentNode, false); + } + else + { + ParentNode->AddChild(CurrentNode, false); + } + } +} + +FEAABB FENaiveSceneGraph::GetNodeAABB(FEAABB& CumulativeAABB, FENaiveSceneGraphNode* TargetNode, std::function Filter) +{ + if (TargetNode == nullptr) + return CumulativeAABB; + + if (TargetNode != Root) + { + if (Filter != nullptr && !Filter(TargetNode->GetEntity())) + return CumulativeAABB; + + FEEntity* Entity = TargetNode->GetEntity(); + FEScene* Scene = Entity->GetParentScene(); + FEAABB NodeAABB = Scene->GetEntityAABB(Entity); + CumulativeAABB = CumulativeAABB.Merge(NodeAABB); + } + + for (size_t i = 0; i < TargetNode->Children.size(); i++) + CumulativeAABB = CumulativeAABB.Merge(GetNodeAABB(CumulativeAABB, TargetNode->GetChildren()[i], Filter)); + + return CumulativeAABB; +} \ No newline at end of file diff --git a/SubSystems/Scene/FENaiveSceneGraph.h b/SubSystems/Scene/FENaiveSceneGraph.h new file mode 100644 index 0000000..b8b27d3 --- /dev/null +++ b/SubSystems/Scene/FENaiveSceneGraph.h @@ -0,0 +1,68 @@ +#pragma once +#include "FENaiveSceneGraphNode.h" +#include + +namespace FocalEngine +{ + class FENaiveSceneGraph + { + friend class FEScene; + + FENaiveSceneGraph(); + ~FENaiveSceneGraph(); + public: + FENaiveSceneGraphNode* GetRoot() const; + + std::string AddNode(FEEntity* Entity, bool bPreserveWorldTransform = true); + bool MoveNode(std::string NodeID, std::string NewParentID, bool bPreserveWorldTransform = true); + void DetachNode(FENaiveSceneGraphNode* NodeToDetach, bool bPreserveWorldTransform = true); + void DeleteNode(FENaiveSceneGraphNode* NodeToDelete); + FENaiveSceneGraphNode* DuplicateNode(std::string NodeIDToDuplicate, std::string NewParentID, bool bAddCopyInName = true); + FENaiveSceneGraphNode* DuplicateNode(FENaiveSceneGraphNode* NodeToDuplicate, FENaiveSceneGraphNode* NewParent, bool bAddCopyInName = true); + + FENaiveSceneGraphNode* ImportNode(FENaiveSceneGraphNode* NodeFromDifferentSceneGraph, FENaiveSceneGraphNode* TargetParent = nullptr, std::function Filter = nullptr); + + size_t GetNodeCount(); + + bool IsDescendant(FENaiveSceneGraphNode* PotentialAncestor, FENaiveSceneGraphNode* PotentialDescendant); + bool HasCycle(FENaiveSceneGraphNode* NodeToCheck); + + FENaiveSceneGraphNode* GetNode(std::string ID); + FENaiveSceneGraphNode* GetNodeByEntityID(std::string EntityID); + std::vector GetNodeByName(std::string Name); + + // Will return first parent node that has the specified component + template + FENaiveSceneGraphNode* GetFirstParentNodeWithComponent(FENaiveSceneGraphNode* Node); + + // Will return first child node that has the specified component + template + FENaiveSceneGraphNode* GetFirstChildNodeWithComponent(FENaiveSceneGraphNode* Node); + + void Clear(); + + Json::Value ToJson(std::function Filter = nullptr); + void FromJson(Json::Value Root); + + FEAABB GetNodeAABB(FEAABB& CumulativeAABB, FENaiveSceneGraphNode* TargetNode = nullptr, std::function Filter = nullptr); + private: + FEScene* ParentScene = nullptr; + + bool bClearing = false; + FENaiveSceneGraphNode* Root; + + void AddNodeInternal(FENaiveSceneGraphNode* NodeToAdd, bool bPreserveWorldTransform = true); + void AddNodeInternal(FENaiveSceneGraphNode* Parent, FENaiveSceneGraphNode* NodeToAdd, bool bPreserveWorldTransform = true); + + bool HasCycleInternal(FENaiveSceneGraphNode* NodeToCheck, + std::unordered_set& Visited, + std::unordered_set& RecursionStack); + + std::vector GetAllNodes(); + + void Initialize(FEScene* Scene); + + bool DuplicateNodeInternal(FENaiveSceneGraphNode* Parent, FENaiveSceneGraphNode* NodeToDuplicate, bool bAddCopyInName = true); + }; +#include "FENaiveSceneGraph.inl" +} \ No newline at end of file diff --git a/SubSystems/Scene/FENaiveSceneGraph.inl b/SubSystems/Scene/FENaiveSceneGraph.inl new file mode 100644 index 0000000..107fd31 --- /dev/null +++ b/SubSystems/Scene/FENaiveSceneGraph.inl @@ -0,0 +1,38 @@ +#pragma once + +template +FENaiveSceneGraphNode* FENaiveSceneGraph::GetFirstParentNodeWithComponent(FENaiveSceneGraphNode* Node) +{ + if (Node == nullptr) + return nullptr; + + FENaiveSceneGraphNode* CurrentNode = Node; + while (CurrentNode->GetParent() != nullptr) + { + CurrentNode = CurrentNode->GetParent(); + if (CurrentNode->GetEntity() == nullptr) + return nullptr; + + if (CurrentNode->GetEntity()->HasComponent()) + return CurrentNode; + } + + return nullptr; +} + +template +FENaiveSceneGraphNode* FENaiveSceneGraph::GetFirstChildNodeWithComponent(FENaiveSceneGraphNode* Node) +{ + if (Node == nullptr) + return nullptr; + + for (FENaiveSceneGraphNode* Child : Node->GetChildren()) + { + if (Child->GetEntity()->HasComponent()) + { + return Child; + } + } + + return nullptr; +} \ No newline at end of file diff --git a/SubSystems/Scene/FENaiveSceneGraphNode.cpp b/SubSystems/Scene/FENaiveSceneGraphNode.cpp new file mode 100644 index 0000000..516d697 --- /dev/null +++ b/SubSystems/Scene/FENaiveSceneGraphNode.cpp @@ -0,0 +1,272 @@ +#include "FENaiveSceneGraphNode.h" +#include "FEScene.h" +using namespace FocalEngine; + +FENaiveSceneGraphNode::FENaiveSceneGraphNode(std::string Name) : FEObject(FE_SCENE_GRAPH_NODE, Name) {} +FENaiveSceneGraphNode::~FENaiveSceneGraphNode() +{ + if (Entity != nullptr) + delete Entity; + + for (size_t i = 0; i < Children.size(); i++) + { + delete Children[i]; + } + Children.clear(); +} + +void FENaiveSceneGraphNode::ApplyTransformHierarchy(FENaiveSceneGraphNode* NodeToWorkOn) +{ + if (NodeToWorkOn == nullptr) + return; + + FETransformComponent& ChildTransform = NodeToWorkOn->GetEntity()->GetComponent(); + glm::mat4 ChildWorldMatrix = ChildTransform.GetWorldMatrix(); + glm::mat4 ParentWorldMatrix = GetEntity()->GetComponent().GetWorldMatrix(); + + // Calculate the inverse of the parent's world matrix + glm::mat4 ParentWorldInverseMatrix = glm::inverse(ParentWorldMatrix); + + // Calculate the new local matrix for the child + glm::mat4 ChildLocalMatrix = ParentWorldInverseMatrix * ChildWorldMatrix; + + glm::dvec3 DoubleScale; + glm::dquat DoubleRotation; + glm::dvec3 DoubleTranslation; + if (GEOMETRY.DecomposeMatrixToTranslationRotationScale(ChildLocalMatrix, DoubleTranslation, DoubleRotation, DoubleScale)) + { + ChildTransform.SetPosition(DoubleTranslation); + ChildTransform.SetQuaternion(DoubleRotation); + ChildTransform.SetScale(DoubleScale); + } +} + +void FENaiveSceneGraphNode::AddChild(FENaiveSceneGraphNode* NodeToAdd, bool bPreserveWorldTransform) +{ + if (NodeToAdd == nullptr || NodeToAdd == this || NodeToAdd->GetParent() == this) + return; + + // Check if the child is already in the children list + for (size_t i = 0; i < Children.size(); i++) + { + if (Children[i] == NodeToAdd) + return; + } + + if (bPreserveWorldTransform && GetEntity() != nullptr) + ApplyTransformHierarchy(NodeToAdd); + + Children.push_back(NodeToAdd); + NodeToAdd->Parent = this; +} + +void FENaiveSceneGraphNode::ReverseTransformHierarchy(FENaiveSceneGraphNode* NodeToWorkOn) +{ + FENaiveSceneGraphNode* OldParent = NodeToWorkOn->Parent; + FETransformComponent& ChildTransform = NodeToWorkOn->GetEntity()->GetComponent(); + + glm::mat4 ChildWorldMatrix = ChildTransform.GetWorldMatrix(); + + // We want preserve the world position of the child + // First we need to check if current parent is not root node + if (OldParent != nullptr && OldParent->GetParent() != nullptr) + { + // In case it is not root we need to reverce old parent influence. + if (OldParent->Parent != nullptr) + { + glm::mat4 ChildLocalMatrix = ChildTransform.GetLocalMatrix(); + glm::mat4 OldParentWorldMatrix = OldParent->GetEntity()->GetComponent().GetWorldMatrix(); + + // Calculate the child's world matrix + ChildWorldMatrix = OldParentWorldMatrix * ChildLocalMatrix; + + glm::dvec3 DoubleScale; + glm::dquat DoubleRotation; + glm::dvec3 DoubleTranslation; + if (GEOMETRY.DecomposeMatrixToTranslationRotationScale(ChildWorldMatrix, DoubleTranslation, DoubleRotation, DoubleScale)) + { + ChildTransform.SetPosition(DoubleTranslation); + ChildTransform.SetQuaternion(DoubleRotation); + ChildTransform.SetScale(DoubleScale); + } + } + } +} + +void FENaiveSceneGraphNode::DetachChild(FENaiveSceneGraphNode* Child, bool bPreserveWorldTransform) +{ + for (size_t i = 0; i < Children.size(); i++) + { + if (Children[i] == Child) + { + if (bPreserveWorldTransform) + ReverseTransformHierarchy(Child); + Child->Parent = nullptr; + Children.erase(Children.begin() + i); + return; + } + } + + // If we reach here, the child was not found + // It is possible that the child is a child of a child + // So we need to search recursively + for (size_t i = 0; i < Children.size(); i++) + { + Children[i]->DetachChild(Child, bPreserveWorldTransform); + } +} + +FENaiveSceneGraphNode* FENaiveSceneGraphNode::GetChild(std::string ID) +{ + for (size_t i = 0; i < Children.size(); i++) + { + if (Children[i]->GetObjectID() == ID) + return Children[i]; + } + + // If we reach here, the child was not found + // It is possible that the child is a child of a child + // So we need to search recursively + for (size_t i = 0; i < Children.size(); i++) + { + FENaiveSceneGraphNode* Child = Children[i]->GetChild(ID); + if (Child != nullptr) + return Child; + } + + return nullptr; +} + +std::vector FENaiveSceneGraphNode::GetChildByName(std::string Name) +{ + std::vector Result; + + for (size_t i = 0; i < Children.size(); i++) + { + if (Children[i]->GetName() == Name) + Result.push_back(Children[i]); + } + + for (size_t i = 0; i < Children.size(); i++) + { + std::vector SubResult = Children[i]->GetChildByName(Name); + for (size_t j = 0; j < SubResult.size(); j++) + Result.push_back(SubResult[j]); + } + + return Result; +} + +std::vector FENaiveSceneGraphNode::GetChildren() +{ + return Children; +} + +std::vector FENaiveSceneGraphNode::GetRecursiveChildren() +{ + std::vector Result; + for (size_t i = 0; i < Children.size(); i++) + { + Result.push_back(Children[i]); + std::vector SubResult = Children[i]->GetRecursiveChildren(); + for (size_t j = 0; j < SubResult.size(); j++) + Result.push_back(SubResult[j]); + } + return Result; +} + +FEEntity* FENaiveSceneGraphNode::GetEntity() +{ + return Entity; +} + +FENaiveSceneGraphNode* FENaiveSceneGraphNode::GetParent() +{ + return Parent; +} + +size_t FENaiveSceneGraphNode::GetImediateChildrenCount() +{ + return Children.size(); +} + +size_t FENaiveSceneGraphNode::GetRecursiveChildCount() +{ + size_t Count = Children.size(); + for (size_t i = 0; i < Children.size(); i++) + { + Count += Children[i]->GetRecursiveChildCount(); + } + return Count; +} + +std::vector FENaiveSceneGraphNode::GetAllNodesInternal() +{ + std::vector Result; + Result.push_back(this); + + for (size_t i = 0; i < Children.size(); i++) + { + std::vector SubResult = Children[i]->GetAllNodesInternal(); + for (size_t j = 0; j < SubResult.size(); j++) + Result.push_back(SubResult[j]); + } + + return Result; +} + +Json::Value FENaiveSceneGraphNode::ToJson(std::function ChildFilter) +{ + Json::Value Node; + Node["Name"] = GetName(); + Node["ID"] = GetObjectID(); + // Only root node does not have ParentID + Node["ParentID"] = Parent->GetObjectID(); + Node["Entity"] = Entity->ToJson(); + + Json::Value ChildrenArray; + for (size_t i = 0; i < Children.size(); i++) + { + if (ChildFilter != nullptr && !ChildFilter(Children[i]->Entity)) + continue; + + ChildrenArray[std::to_string(i)]["ID"] = Children[i]->GetObjectID(); + } + Node["Children"] = ChildrenArray; + + return Node; +} + +void FENaiveSceneGraphNode::FromJson(Json::Value Root) +{ + if (Entity == nullptr) + { + LOG.Add("FENaiveSceneGraphNode::FromJson called but Entity is nullptr", "FE_LOG_LOADING", FE_LOG_ERROR); + return; + } + + SetName(Root["Name"].asString()); + SetID(Root["ID"].asString()); + Entity->FromJson(Root["Entity"]); +} + +FENaiveSceneGraphNode* FENaiveSceneGraphNode::GetChildByEntityID(std::string EntityID) +{ + for (size_t i = 0; i < Children.size(); i++) + { + if (Children[i]->Entity->GetObjectID() == EntityID) + return Children[i]; + } + + // If we reach here, the child was not found + // It is possible that the child is a child of a child + // So we need to search recursively + for (size_t i = 0; i < Children.size(); i++) + { + FENaiveSceneGraphNode* Child = Children[i]->GetChildByEntityID(EntityID); + if (Child != nullptr) + return Child; + } + + return nullptr; +} \ No newline at end of file diff --git a/SubSystems/Scene/FENaiveSceneGraphNode.h b/SubSystems/Scene/FENaiveSceneGraphNode.h new file mode 100644 index 0000000..6408079 --- /dev/null +++ b/SubSystems/Scene/FENaiveSceneGraphNode.h @@ -0,0 +1,43 @@ +#pragma once + +#include "../ResourceManager/FEResourceManager.h" +#include "FEEntity.h" + +namespace FocalEngine +{ + class FENaiveSceneGraphNode : public FEObject + { + friend class FENaiveSceneGraph; + friend class FEScene; + public: + FENaiveSceneGraphNode* GetParent(); + + void AddChild(FENaiveSceneGraphNode* Child, bool bPreserveWorldTransform = true); + void DetachChild(FENaiveSceneGraphNode* Child, bool bPreserveWorldTransform = true); + FENaiveSceneGraphNode* GetChild(std::string ID); + FENaiveSceneGraphNode* GetChildByEntityID(std::string EntityID); + std::vector GetChildByName(std::string Name); + size_t GetImediateChildrenCount(); + size_t GetRecursiveChildCount(); + + std::vector GetChildren(); + std::vector GetRecursiveChildren(); + + FEEntity* GetEntity(); + + Json::Value ToJson(std::function ChildFilter = nullptr); + void FromJson(Json::Value Root); + private: + FENaiveSceneGraphNode(std::string Name = "Unnamed NaiveSceneNode"); + ~FENaiveSceneGraphNode(); + + FENaiveSceneGraphNode* Parent = nullptr; + std::vector Children; + FEEntity* Entity = nullptr; + + void ApplyTransformHierarchy(FENaiveSceneGraphNode* NodeToWorkOn); + void ReverseTransformHierarchy(FENaiveSceneGraphNode* NodeToWorkOn); + + std::vector GetAllNodesInternal(); + }; +} \ No newline at end of file diff --git a/SubSystems/Scene/FEPrefab.cpp b/SubSystems/Scene/FEPrefab.cpp new file mode 100644 index 0000000..77ebf9f --- /dev/null +++ b/SubSystems/Scene/FEPrefab.cpp @@ -0,0 +1,74 @@ +#include "FEPrefab.h" +#include "../SubSystems/Scene/FESceneManager.h" +#include "../ResourceManager/FEResourceManager.h" +using namespace FocalEngine; + +FEPrefab::FEPrefab(const std::string Name, bool bSceneAllocation) : FEObject(FE_PREFAB, Name) +{ + SetName(Name); + if (bSceneAllocation) + { + Scene = SCENE_MANAGER.CreateScene(GetName() + "_Scene", "", FESceneFlag::PrefabDescription); + RESOURCE_MANAGER.SetTag(Scene, PREFAB_SCENE_DESCRIPTION_TAG); + } + + SetDirtyFlag(true); +} + +FEPrefab::~FEPrefab() +{ + SCENE_MANAGER.DeleteScene(Scene); +} + +FEAABB FEPrefab::GetAABB() +{ + return Scene->GetSceneAABB(); +} + +bool FEPrefab::IsUsingMaterial(const std::string MaterialID) const +{ + if (Scene == nullptr) + return false; + + std::vector Entities = Scene->GetEntityListWithComponent(); + for (int i = 0; i < Entities.size(); i++) + { + FEGameModelComponent& GameModelComponent = Entities[i]->GetComponent(); + if (GameModelComponent.GetGameModel()->Material->GetObjectID() == MaterialID) + return true; + + if (GameModelComponent.GetGameModel()->BillboardMaterial != nullptr && GameModelComponent.GetGameModel()->BillboardMaterial->GetObjectID() == MaterialID) + return true; + } + + return false; +} + +bool FEPrefab::IsUsingGameModel(const std::string GameModelID) const +{ + if (Scene == nullptr) + return false; + + std::vector Entities = Scene->GetEntityListWithComponent(); + for (int i = 0; i < Entities.size(); i++) + { + FEGameModelComponent& GameModelComponent = Entities[i]->GetComponent(); + if (GameModelComponent.GetGameModel()->GetObjectID() == GameModelID) + return true; + } + + return false; +} + +FEScene* FEPrefab::GetScene() const +{ + return Scene; +} + +void FEPrefab::SetScene(FEScene* Scene, bool DeleteOldScene) +{ + if (DeleteOldScene) + SCENE_MANAGER.DeleteScene(this->Scene); + + this->Scene = Scene; +} \ No newline at end of file diff --git a/SubSystems/Scene/FEPrefab.h b/SubSystems/Scene/FEPrefab.h new file mode 100644 index 0000000..7c73a90 --- /dev/null +++ b/SubSystems/Scene/FEPrefab.h @@ -0,0 +1,32 @@ +#pragma once + +#ifndef FEPREFAB_H +#define FEPREFAB_H +#include "../Renderer/FEGameModel.h" + +namespace FocalEngine +{ +#define PREFAB_SCENE_DESCRIPTION_TAG "PREFAB_DESCRIPTION" + + class FEPrefab : public FEObject + { + friend FERenderer; + friend FEResourceManager; + friend FEEntity; + + FEScene* Scene = nullptr; + public: + FEPrefab(std::string Name = "", bool bSceneAllocation = true); + ~FEPrefab(); + + FEAABB GetAABB(); + + bool IsUsingMaterial(std::string MaterialID) const; + bool IsUsingGameModel(std::string GameModelID) const; + + void SetScene(FEScene* Scene, bool DeleteOldScene = true); + FEScene* GetScene() const; + }; +} + +#endif FEPREFAB_H \ No newline at end of file diff --git a/SubSystems/Scene/FEScene.cpp b/SubSystems/Scene/FEScene.cpp index 47c558e..af158c6 100644 --- a/SubSystems/Scene/FEScene.cpp +++ b/SubSystems/Scene/FEScene.cpp @@ -1,456 +1,682 @@ #include "FEScene.h" -using namespace FocalEngine; +#include "Components/Systems/FEComponentSystems.h" -FEScene* FEScene::Instance = nullptr; +using namespace FocalEngine; -FEScene::FEScene() +FEScene::FEScene() : FEObject(FE_OBJECT_TYPE::FE_SCENE, "Unnamed Scene") { + SceneGraph.Initialize(this); } -FELight* FEScene::AddLight(const FE_OBJECT_TYPE LightType, std::string Name, const std::string ForceObjectID) +FEScene::~FEScene() { - if (Name.empty()) - Name = "unnamedLight"; - - if (LightType == FE_DIRECTIONAL_LIGHT) - { - FEDirectionalLight* NewLight = new FEDirectionalLight(); - if (!ForceObjectID.empty()) - NewLight->SetID(ForceObjectID); - NewLight->SetName(Name); - - NewLight->CascadeData[0].FrameBuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_DEPTH_ATTACHMENT, 1024 * 2, 1024 * 2); - NewLight->CascadeData[1].FrameBuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_DEPTH_ATTACHMENT, 1024 * 2, 1024 * 2); - NewLight->CascadeData[2].FrameBuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_DEPTH_ATTACHMENT, 1024 * 2, 1024 * 2); - NewLight->CascadeData[3].FrameBuffer = RESOURCE_MANAGER.CreateFramebuffer(FE_DEPTH_ATTACHMENT, 1024 * 2, 1024 * 2); - - // to clear cascades framebuffer - NewLight->SetCastShadows(false); - NewLight->SetCastShadows(true); + Clear(); +} - return NewLight; - } - else if (LightType == FE_SPOT_LIGHT) +#include "FESceneManager.h" +void FEScene::SetFlag(FESceneFlag Flag, bool Value) +{ + if (Value) + Flags |= Flag; + else + Flags = static_cast( + static_cast>(Flags) & + ~static_cast>(Flag) + ); + + if (HasFlag(FESceneFlag::Active)) { - FESpotLight* NewLight = new FESpotLight(); - if (!ForceObjectID.empty()) - NewLight->SetID(ForceObjectID); - NewLight->SetName(Name); - - return NewLight; + SCENE_MANAGER.RegisterAllComponentCallbacks(this); } - else if (LightType == FE_POINT_LIGHT) + else { - FEPointLight* NewLight = new FEPointLight(); - if (!ForceObjectID.empty()) - NewLight->SetID(ForceObjectID); - NewLight->SetName(Name); - - return NewLight; + SCENE_MANAGER.UnRegisterAllComponentCallbacks(this); } - - return nullptr; } -FEEntity* FEScene::AddEntity(FEGameModel* GameModel, std::string Name, const std::string ForceObjectID) +bool FEScene::HasFlag(FESceneFlag Flag) const { - if (Name.empty()) - Name = "unnamedEntity"; - - FEEntity* NewEntity = RESOURCE_MANAGER.CreateEntity(GameModel, Name, ForceObjectID); - EntityMap[NewEntity->GetObjectID()] = NewEntity; - return EntityMap[NewEntity->GetObjectID()]; + return (static_cast>(Flags) & + static_cast>(Flag)) != 0; } -FEEntity* FEScene::AddEntity(FEPrefab* Prefab, std::string Name, const std::string ForceObjectID) +FEEntity* FEScene::GetEntity(std::string ID) { - if (Name.empty()) - Name = "unnamedEntity"; + if (EntityMap.find(ID) == EntityMap.end()) + return nullptr; - FEEntity* NewEntity = RESOURCE_MANAGER.CreateEntity(Prefab, Name, ForceObjectID); - EntityMap[NewEntity->GetObjectID()] = NewEntity; - return EntityMap[NewEntity->GetObjectID()]; + return EntityMap[ID]; } -bool FEScene::AddEntity(FEEntity* NewEntity) +std::vector FEScene::GetEntityByName(const std::string Name) { - if (NewEntity == nullptr) - return false; + std::vector Result; - if (NewEntity->Prefab == nullptr) - return false; + auto EntityIterator = EntityMap.begin(); + while (EntityIterator != EntityMap.end()) + { + if (EntityIterator->second->GetName() == Name) + Result.push_back(EntityIterator->second); - EntityMap[NewEntity->GetObjectID()] = NewEntity; + EntityIterator++; + } - return true; + return Result; } -FEEntity* FEScene::GetEntity(const std::string ID) +void FEScene::DeleteEntity(std::string ID) { if (EntityMap.find(ID) == EntityMap.end()) - return nullptr; + return; - return EntityMap[ID]; + DeleteEntity(EntityMap[ID]); } -std::vector FEScene::GetEntityByName(const std::string Name) +void FEScene::DeleteEntity(FEEntity* Entity) { - std::vector result; - - auto it = EntityMap.begin(); - while (it != EntityMap.end()) + if (Entity == nullptr) { - if (it->second->GetType() != FE_ENTITY) - { - it++; - continue; - } - - if (it->second->GetName() == Name) - { - result.push_back(it->second); - } + LOG.Add("Call of FEScene::DeleteEntity with nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return; + } - it++; + std::string EntityID = Entity->ID; + FENaiveSceneGraphNode* GraphNode = SceneGraph.GetNodeByEntityID(EntityID); + if (GraphNode == nullptr) + { + LOG.Add("Entity does not have a graph node in FEScene::DeleteEntity", "FE_LOG_ECS", FE_LOG_WARNING); + return; } - return result; + SceneGraph.DeleteNode(GraphNode); } -void FEScene::DeleteEntity(const std::string ID) +void FEScene::ClearEntityRecords(std::string EntityID, entt::entity EnttEntity) { - if (EntityMap.find(ID) == EntityMap.end()) - return; - - const FEEntity* EntityToDelete = EntityMap[ID]; - delete EntityToDelete; - EntityMap.erase(ID); + EntityMap.erase(EntityID); + EnttToEntity.erase(EnttEntity); } -std::vector FEScene::GetEntityList() +std::vector FEScene::GetEntityIDList() { FE_MAP_TO_STR_VECTOR(EntityMap) } -FELight* FEScene::GetLight(const std::string ID) +void FEScene::Clear() { - if (OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].find(ID) != OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].end()) - return reinterpret_cast(OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT][ID]); + bIsSceneClearing = true; - if (OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT].find(ID) != OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT].end()) - return reinterpret_cast(OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT][ID]); + SceneGraph.Clear(); - if (OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT].find(ID) != OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT].end()) - return reinterpret_cast(OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT][ID]); - - return nullptr; + // Some entities could be not in the scene graph, so we need to delete them manually. + auto EntityIterator = EntityMap.begin(); + while (EntityIterator != EntityMap.end()) + { + delete EntityIterator->second; + EntityIterator++; + } + EntityMap.clear(); + EnttToEntity.clear(); + + // Force clear registry. + Registry = entt::registry{}; + + bIsSceneClearing = false; } -std::vector FEScene::GetLightsList() +// In case that game model is used in some entities, we need to replace it with default game model. +// TODO: Implement more efficient solution without iterating through all entities. +void FEScene::PrepareForGameModelDeletion(const FEGameModel* GameModel) { - std::vector result; - auto iterator = OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].begin(); - while (iterator != OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].end()) - { - result.push_back(iterator->first); - iterator++; - } + auto GameModelComponentsView = this->Registry.view(); - iterator = OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT].begin(); - while (iterator != OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT].end()) + for (entt::entity EnTTEntity : GameModelComponentsView) { - result.push_back(iterator->first); - iterator++; + FEGameModelComponent& GameModelComponent = GameModelComponentsView.get(EnTTEntity); + FEEntity* Entity = this->GetEntityByEnTT(EnTTEntity); + if (GameModelComponent.GetGameModel() == nullptr) + continue; + + if (GameModelComponent.GetGameModel() == GameModel) + GameModelComponent.SetGameModel(RESOURCE_MANAGER.GetGameModel(RESOURCE_MANAGER.GetEnginePrivateGameModelIDList()[0])); } +} + +// In case that prefab is used in some entities, we need to delete it's reference. +// TODO: Implement more efficient solution without iterating through all entities. +void FEScene::PrepareForPrefabDeletion(const FEPrefab* Prefab) +{ + auto PrefabInstanceComponentsView = this->Registry.view(); - iterator = OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT].begin(); - while (iterator != OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT].end()) + for (entt::entity EnTTEntity : PrefabInstanceComponentsView) { - result.push_back(iterator->first); - iterator++; - } + FEPrefabInstanceComponent& PrefabInstanceComponent = PrefabInstanceComponentsView.get(EnTTEntity); + FEEntity* Entity = this->GetEntityByEnTT(EnTTEntity); + if (PrefabInstanceComponent.GetPrefab() == nullptr) + continue; - return result; + if (PrefabInstanceComponent.GetPrefab() == Prefab) + Entity->RemoveComponent(); + } } -void FEScene::Clear() +std::vector FEScene::ImportAsset(std::string FileName) { - auto EntityIterator = EntityMap.begin(); - while (EntityIterator != EntityMap.end()) + std::vector Result; + + if (FileName.empty()) { - delete EntityIterator->second; - EntityIterator++; + LOG.Add("call of FEScene::ImportAsset with empty FileName", "FE_LOG_LOADING", FE_LOG_ERROR); + return Result; } - EntityMap.clear(); - std::vector AllLights; - auto iterator = OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].begin(); - while (iterator != OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].end()) + if (!FILE_SYSTEM.DoesFileExist(FileName)) { - AllLights.push_back(iterator->second); - iterator++; + LOG.Add("Can't locate file: " + std::string(FileName) + " in FEScene::ImportAsset", "FE_LOG_LOADING", FE_LOG_ERROR); + return Result; } - iterator = OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT].begin(); - while (iterator != OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT].end()) + std::string FileExtention = FILE_SYSTEM.GetFileExtension(FileName); + std::transform(FileExtention.begin(), FileExtention.end(), FileExtention.begin(), [](const unsigned char C) { return std::tolower(C); }); + + if (FileExtention == ".png" || FileExtention == ".jpg" || FileExtention == ".bmp") { - AllLights.push_back(iterator->second); - iterator++; + FETexture* LoadedTexture = RESOURCE_MANAGER.ImportTexture(FileName.c_str()); + if (LoadedTexture != nullptr) + Result.push_back(LoadedTexture); + } + else if (FileExtention == ".obj") + { + std::vector LoadedObjects = RESOURCE_MANAGER.ImportOBJ(FileName.c_str(), true); + Result.insert(Result.end(), LoadedObjects.begin(), LoadedObjects.end()); + } + // .gltf could contain scene, so EntityIterator should be loaded in FEScene + else if (FileExtention == ".gltf") + { + std::vector LoadedObjects = LoadGLTF(FileName); + Result.insert(Result.end(), LoadedObjects.begin(), LoadedObjects.end()); } - iterator = OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT].begin(); - while (iterator != OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT].end()) + return Result; +} + +std::vector FEScene::LoadGLTF(std::string FileName) +{ + std::vector Result; + if (!FILE_SYSTEM.DoesFileExist(FileName)) { - AllLights.push_back(iterator->second); - iterator++; + LOG.Add("call of FEScene::LoadGLTF can't locate file: " + std::string(FileName), "FE_LOG_LOADING", FE_LOG_ERROR); + return Result; } - for (size_t i = 0; i < AllLights.size(); i++) + FEGLTFLoader& GLTF = FEGLTFLoader::GetInstance(); + GLTF.Load(FileName.c_str()); + std::string Directory = FILE_SYSTEM.GetDirectoryPath(FileName); + + std::unordered_map AlreadyLoadedTextures; + std::unordered_map TextureMap; + for (size_t i = 0; i < GLTF.Textures.size(); i++) { - if (AllLights[i]->GetType() == FE_DIRECTIONAL_LIGHT) + int ImageIndex = GLTF.Textures[i].Source; + if (ImageIndex < 0 || ImageIndex >= GLTF.Images.size()) { - delete reinterpret_cast(AllLights[i]); + LOG.Add("FEScene::LoadGLTF image index out of bounds", "FE_LOG_LOADING", FE_LOG_ERROR); + continue; } - else if (AllLights[i]->GetType() == FE_POINT_LIGHT) + + std::string FullPath = Directory + "\\" + GLTF.Images[ImageIndex].Uri; + if (AlreadyLoadedTextures.find(FullPath) != AlreadyLoadedTextures.end()) { - delete reinterpret_cast(AllLights[i]); + TextureMap[static_cast(TextureMap.size())] = AlreadyLoadedTextures[FullPath]; + continue; } - else if (AllLights[i]->GetType() == FE_SPOT_LIGHT) + + if (!FILE_SYSTEM.DoesFileExist(FullPath.c_str())) { - delete reinterpret_cast(AllLights[i]); + TextureMap[static_cast(TextureMap.size())] = nullptr; + continue; + } + + FETexture* LoadedTexture = RESOURCE_MANAGER.ImportTexture(FullPath.c_str()); + //FETexture* LoadedTexture = RESOURCE_MANAGER.NoTexture; + if (LoadedTexture != nullptr) + { + if (!GLTF.Textures[i].Name.empty()) + { + LoadedTexture->SetName(GLTF.Textures[i].Name); + } + else if (!GLTF.Images[ImageIndex].Name.empty()) + { + LoadedTexture->SetName(GLTF.Images[ImageIndex].Name); + } + + TextureMap[static_cast(TextureMap.size())] = LoadedTexture; + AlreadyLoadedTextures[FullPath] = LoadedTexture; + Result.push_back(LoadedTexture); } } - auto TerrainIterator = TerrainMap.begin(); - while (TerrainIterator != TerrainMap.end()) + std::unordered_map MaterialsMap; + for (size_t i = 0; i < GLTF.Materials.size(); i++) { - delete TerrainIterator->second; - TerrainIterator++; + FEMaterial* NewMaterial = RESOURCE_MANAGER.CreateMaterial(GLTF.Materials[i].Name); + MaterialsMap[static_cast(i)] = NewMaterial; + NewMaterial->Shader = RESOURCE_MANAGER.GetShader("0800253C242B05321A332D09"/*"FEPBRShader"*/); + + if (TextureMap.find(GLTF.Materials[i].PBRMetallicRoughness.BaseColorTexture.Index) != TextureMap.end() && TextureMap[GLTF.Materials[i].PBRMetallicRoughness.BaseColorTexture.Index] != nullptr) + { + NewMaterial->AddTexture(TextureMap[GLTF.Materials[i].PBRMetallicRoughness.BaseColorTexture.Index]); + NewMaterial->SetAlbedoMap(TextureMap[GLTF.Materials[i].PBRMetallicRoughness.BaseColorTexture.Index]); + } + else if (GLTF.Materials[i].PBRMetallicRoughness.BaseColorFactor[0] != -1) + { + NewMaterial->SetBaseColor(GLTF.Materials[i].PBRMetallicRoughness.BaseColorFactor); + } + + if (TextureMap.find(GLTF.Materials[i].PBRMetallicRoughness.MetallicRoughnessTexture.Index) != TextureMap.end() && TextureMap[GLTF.Materials[i].PBRMetallicRoughness.MetallicRoughnessTexture.Index] != nullptr) + { + /* + https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/Specification.adoc#reference-material + The textures for metalness and roughness properties are packed together in a single texture called metallicRoughnessTexture. + Its green channel contains roughness values and its blue channel contains metalness values. + */ + NewMaterial->AddTexture(TextureMap[GLTF.Materials[i].PBRMetallicRoughness.MetallicRoughnessTexture.Index]); + NewMaterial->SetRoughnessMap(TextureMap[GLTF.Materials[i].PBRMetallicRoughness.MetallicRoughnessTexture.Index], 1, 0); + NewMaterial->SetMetalnessMap(TextureMap[GLTF.Materials[i].PBRMetallicRoughness.MetallicRoughnessTexture.Index], 2, 0); + } + + if (TextureMap.find(GLTF.Materials[i].NormalTexture.Index) != TextureMap.end() && TextureMap[GLTF.Materials[i].NormalTexture.Index] != nullptr) + { + NewMaterial->AddTexture(TextureMap[GLTF.Materials[i].NormalTexture.Index]); + NewMaterial->SetNormalMap(TextureMap[GLTF.Materials[i].NormalTexture.Index]); + NewMaterial->SetNormalMapIntensity(GLTF.Materials[i].NormalTexture.Scale); + } + + if (TextureMap.find(GLTF.Materials[i].OcclusionTexture.Index) != TextureMap.end() && TextureMap[GLTF.Materials[i].OcclusionTexture.Index] != nullptr) + { + NewMaterial->AddTexture(TextureMap[GLTF.Materials[i].OcclusionTexture.Index]); + NewMaterial->SetAOMap(TextureMap[GLTF.Materials[i].OcclusionTexture.Index]); + NewMaterial->SetAmbientOcclusionMapIntensity(GLTF.Materials[i].OcclusionTexture.Strength); + } + + Result.push_back(NewMaterial); } - TerrainMap.clear(); -} -void FEScene::PrepareForGameModelDeletion(const FEGameModel* GameModel) -{ - // looking if this gameModel is used in some prefab - // to-do: should be done through list of pointers to entities that uses this gameModel. - const auto PrefabList = RESOURCE_MANAGER.GetPrefabList(); - for (size_t i = 0; i < PrefabList.size(); i++) + // Each Primitive need to have GameModel, there could be multiple Primitives in one glTFMesh. + std::unordered_map> GLTFMeshesToGameModelMap; + for (size_t i = 0; i < GLTF.Meshes.size(); i++) { - FEPrefab* CurrentPrefab = RESOURCE_MANAGER.GetPrefab(PrefabList[i]); - for (int j = 0; j < CurrentPrefab->ComponentsCount(); j++) + GLTFMeshesToGameModelMap[static_cast(i)] = std::vector(); + GLTFMeshesToGameModelMap[static_cast(i)].resize(GLTF.Meshes[i].Primitives.size()); + + for (size_t j = 0; j < GLTF.Meshes[i].Primitives.size(); j++) { - if (CurrentPrefab->GetComponent(j)->GameModel == GameModel) + GLTFMeshesToGameModelMap[static_cast(i)][j] = nullptr; + + if (GLTF.Meshes[i].Primitives[j].RawData.Indices.empty()) + { + LOG.Add("Primitive[" + std::to_string(j) + "].RawData.Indices is empty in function FEScene::LoadGLTF.", "FE_LOG_LOADING", FE_LOG_ERROR); + continue; + } + + if (GLTF.Meshes[i].Primitives[j].RawData.Positions.empty()) + { + LOG.Add("Primitive[" + std::to_string(j) + "].RawData.Positions is empty in function FEScene::LoadGLTF.", "FE_LOG_LOADING", FE_LOG_ERROR); + continue; + } + + if (GLTF.Meshes[i].Primitives[j].RawData.Normals.empty()) { - CurrentPrefab->GetComponent(j)->GameModel = RESOURCE_MANAGER.GetGameModel(RESOURCE_MANAGER.GetStandardGameModelList()[0]); - CurrentPrefab->SetDirtyFlag(true); + LOG.Add("Primitive[" + std::to_string(j) + "].RawData.Normals is empty in function FEScene::LoadGLTF. Trying to calculate normals.", "FE_LOG_LOADING", FE_LOG_WARNING); + + GLTF.Meshes[i].Primitives[j].RawData.Normals.resize(GLTF.Meshes[i].Primitives[j].RawData.Positions.size()); + GEOMETRY.CalculateNormals(GLTF.Meshes[i].Primitives[j].RawData.Indices, GLTF.Meshes[i].Primitives[0].RawData.Positions, GLTF.Meshes[i].Primitives[j].RawData.Normals); + } + + if (GLTF.Meshes[i].Primitives[j].RawData.Tangents.empty()) + { + LOG.Add("Primitive[" + std::to_string(j) + "].RawData.Tangents is empty in function FEScene::LoadGLTF. Trying to calculate tangents.", "FE_LOG_LOADING", FE_LOG_WARNING); + + if (GLTF.Meshes[i].Primitives[j].RawData.UVs.empty()) + { + LOG.Add("Primitive[" + std::to_string(j) + "].RawData.UVs is empty in function FEScene::LoadGLTF. Can't calculate tangents.", "FE_LOG_LOADING", FE_LOG_ERROR); + continue; + } + + GLTF.Meshes[i].Primitives[j].RawData.Tangents.resize(GLTF.Meshes[i].Primitives[j].RawData.Positions.size()); + GEOMETRY.CalculateTangents(GLTF.Meshes[i].Primitives[j].RawData.Indices, GLTF.Meshes[i].Primitives[j].RawData.Positions, GLTF.Meshes[i].Primitives[j].RawData.UVs[0], GLTF.Meshes[i].Primitives[j].RawData.Normals, GLTF.Meshes[i].Primitives[j].RawData.Tangents); + } + + if (GLTF.Meshes[i].Primitives[j].Material != -1) + { + int UVIndex = 0; + UVIndex = GLTF.Materials[GLTF.Meshes[i].Primitives[j].Material].PBRMetallicRoughness.BaseColorTexture.TexCoord; + if (GLTF.Meshes[i].Primitives[j].RawData.UVs.size() <= UVIndex) + UVIndex = 0; + } + + Result.push_back(RESOURCE_MANAGER.RawDataToMesh(GLTF.Meshes[i].Primitives[j].RawData.Positions, + GLTF.Meshes[i].Primitives[j].RawData.Normals, + GLTF.Meshes[i].Primitives[j].RawData.Tangents, + GLTF.Meshes[i].Primitives[j].RawData.UVs[0/*UVIndex*/], + GLTF.Meshes[i].Primitives[j].RawData.Indices, + GLTF.Meshes[i].Name)); + + if (GLTF.Meshes[i].Primitives[j].Material != -1) + { + FEGameModel* NewGameModel = RESOURCE_MANAGER.CreateGameModel(reinterpret_cast(Result.back()), MaterialsMap[GLTF.Meshes[i].Primitives[j].Material]); + + std::string GameModelName = "Unnamed Gamemodel"; + std::string PrefabName = "Unnamed Prefab"; + if (!GLTF.Meshes[i].Name.empty()) + { + if (GLTF.Meshes[i].Primitives.size() == 1) + { + GameModelName = GLTF.Meshes[i].Name + "_GameModel"; + PrefabName = GLTF.Meshes[i].Name + "_Prefab"; + } + else + { + GameModelName = GLTF.Meshes[i].Name + "_GameModel_Primitive_" + std::to_string(j); + PrefabName = GLTF.Meshes[i].Name + "_Prefab_Primitive_" + std::to_string(j); + } + } + + NewGameModel->SetName(GameModelName); + Result.push_back(NewGameModel); + + //FEPrefab* NewPrefab = RESOURCE_MANAGER.CreatePrefab(NewGameModel); + //NewPrefab->SetName(PrefabName); + //GLTFMeshesToGameModelMap[static_cast(i)][j] = NewPrefab; + //Result.push_back(NewPrefab); + + GLTFMeshesToGameModelMap[static_cast(i)][j] = NewGameModel; } } } -} -void FEScene::PrepareForPrefabDeletion(const FEPrefab* Prefab) -{ - // looking if this prefab is used in some entities - // to-do: should be done through list of pointers to entities that uses this gameModel. - auto EntitiesIterator = EntityMap.begin(); - while (EntitiesIterator != EntityMap.end()) + if (GLTF.Scene != -1) { - if (EntitiesIterator->second->Prefab == Prefab) + GLTFScene& SceneToLoad = GLTF.Scenes[GLTF.Scene]; + + for (size_t i = 0; i < SceneToLoad.RootChildren.size(); i++) { - EntitiesIterator->second->Prefab = RESOURCE_MANAGER.GetPrefab(RESOURCE_MANAGER.GetStandardPrefabList()[0]); - EntitiesIterator->second->SetDirtyFlag(true); + AddGLTFNodeToSceneGraph(GLTF, GLTF.Nodes[SceneToLoad.RootChildren[i]], GLTFMeshesToGameModelMap, SceneGraph.GetRoot()->GetObjectID()); } - - EntitiesIterator++; } -} - -bool FEScene::AddTerrain(FETerrain* NewTerrain) -{ - if (NewTerrain == nullptr) - return false; - TerrainMap[NewTerrain->GetObjectID()] = NewTerrain; - - return true; + GLTF.Clear(); + return Result; } -std::vector FEScene::GetTerrainList() +std::vector FEScene::AddGLTFNodeToSceneGraph(const FEGLTFLoader& GLTF, const GLTFNodes& Node, const std::unordered_map>& GLTFMeshesToGameModelMap, const std::string ParentID) { - FE_MAP_TO_STR_VECTOR(TerrainMap) -} + std::vector Result; -FETerrain* FEScene::GetTerrain(const std::string ID) -{ - if (TerrainMap.find(ID) == TerrainMap.end()) - return nullptr; + int GLTFMeshToPrefabIndex = -1; + GLTFMeshToPrefabIndex = Node.Mesh; - return TerrainMap[ID]; -} + std::string NodeName = "Unnamed Entity"; + if (!NodeName.empty()) + NodeName = Node.Name; + FEEntity* Entity = CreateEntity(NodeName); -void FEScene::DeleteTerrain(const std::string ID) -{ - if (TerrainMap.find(ID) == TerrainMap.end()) - return; + FETransformComponent& Transform = Entity->GetComponent(); + Transform.SetPosition(Node.Translation); + Transform.SetQuaternion(Node.Rotation); + Transform.SetScale(Node.Scale); - const FETerrain* TerrainToDelete = TerrainMap[ID]; + FENaiveSceneGraphNode* AddedNode = SceneGraph.GetNodeByEntityID(Entity->GetObjectID()); + SceneGraph.MoveNode(AddedNode->GetObjectID(), ParentID, false); - auto EntityIt = EntityMap.begin(); - while (EntityIt != EntityMap.end()) + if (GLTFMeshToPrefabIndex != -1) { - if (EntityIt->second->GetType() == FE_ENTITY_INSTANCED) + if (GLTFMeshesToGameModelMap.find(GLTFMeshToPrefabIndex) == GLTFMeshesToGameModelMap.end()) { - FEEntityInstanced* InstancedEntity = reinterpret_cast(EntityIt->second); - if (InstancedEntity->GetSnappedToTerrain() == TerrainToDelete) + LOG.Add("PrefabMap does not contain GLTFMesheToPrefabIndex in FEScene::LoadGLTF", "FE_LOG_LOADING", FE_LOG_ERROR); + } + + if (GLTFMeshesToGameModelMap.find(GLTFMeshToPrefabIndex)->second.empty()) + { + LOG.Add("GLTFMeshesToPrefabMap[GLTFMesheToPrefabIndex] is empty in FEScene::LoadGLTF", "FE_LOG_LOADING", FE_LOG_ERROR); + } + + std::vector GameModels = GLTFMeshesToGameModelMap.find(GLTFMeshToPrefabIndex)->second; + if (GameModels.size() == 1) + { + Entity->AddComponent(GameModels[0]); + } + else + { + for (size_t i = 0; i < GameModels.size(); i++) { - InstancedEntity->UnSnapFromTerrain(); + std::string CurrentNodeName = NodeName; + CurrentNodeName = NodeName + "_Primitive_" + std::to_string(i); + + FEEntity* ChildEntity = CreateEntity(CurrentNodeName); + ChildEntity->AddComponent(GameModels[i]); + + FENaiveSceneGraphNode* ChildNode = SceneGraph.GetNodeByEntityID(ChildEntity->GetObjectID()); + SceneGraph.MoveNode(ChildNode->GetObjectID(), AddedNode->GetObjectID(), false); } } + } + + Result.push_back(Entity); - EntityIt++; + for (size_t i = 0; i < Node.Children.size(); i++) + { + if (Node.Children[i] < 0 || Node.Children[i] >= GLTF.Nodes.size()) + { + LOG.Add("Node.Children[i] out of bounds in FEScene::AddGLTFNodeToSceneGraph", "FE_LOG_LOADING", FE_LOG_ERROR); + continue; + } + + GLTFNodes ChildNode = GLTF.Nodes[Node.Children[i]]; + std::vector TempResult = AddGLTFNodeToSceneGraph(GLTF, ChildNode, GLTFMeshesToGameModelMap, AddedNode->GetObjectID()); + Result.insert(Result.end(), TempResult.begin(), TempResult.end()); } - - delete TerrainToDelete; - TerrainMap.erase(ID); + + return Result; } -FEEntityInstanced* FEScene::AddEntityInstanced(FEPrefab* Prefab, std::string Name, const std::string ForceObjectID) +FEEntity* FEScene::CreateEntity(std::string Name, std::string ForceObjectID) { - if (Name.empty()) - Name = "unnamedEntityInstanced"; + FEEntity* Result = CreateEntityOrphan(Name, ForceObjectID); + SceneGraph.AddNode(Result, false); - FEEntityInstanced* NewEntityInstanced = new FEEntityInstanced(Prefab, Name); - if (!ForceObjectID.empty()) - NewEntityInstanced->SetID(ForceObjectID); + return Result; +} - EntityMap[NewEntityInstanced->GetObjectID()] = NewEntityInstanced; - return NewEntityInstanced; +FEEntity* FEScene::CreateEntityOrphan(std::string Name, std::string ForceObjectID) +{ + return CreateEntityInternal(Name, ForceObjectID); } -FEEntityInstanced* FEScene::AddEntityInstanced(FEGameModel* GameModel, const std::string Name, const std::string ForceObjectID) +FEEntity* FEScene::CreateEntityInternal(std::string Name, std::string ForceObjectID) { - FEPrefab* TempPrefab = RESOURCE_MANAGER.CreatePrefab(GameModel, GameModel->GetName()); - return AddEntityInstanced(TempPrefab, Name, ForceObjectID); + if (Name.empty()) + Name = "Unnamed Entity"; + + FEEntity* Entity = new FEEntity(Registry.create(), this); + if (!ForceObjectID.empty()) + Entity->SetID(ForceObjectID); + Entity->SetName(Name); + + EntityMap[Entity->GetObjectID()] = Entity; + EnttToEntity[Entity->EnTTEntity] = Entity; + + Entity->AddComponent(); + Entity->AddComponent(); + + return Entity; } -bool FEScene::AddEntityInstanced(FEEntityInstanced* NewEntityInstanced) +FEEntity* FEScene::GetEntityByEnTT(entt::entity ID) { - if (NewEntityInstanced == nullptr) - return false; + if (EnttToEntity.find(ID) == EnttToEntity.end()) + return nullptr; - if (NewEntityInstanced->Prefab == nullptr) - return false; + return EnttToEntity[ID]; +} - EntityMap[NewEntityInstanced->GetObjectID()] = NewEntityInstanced; +void FEScene::Update() +{ - return true; } -FEEntityInstanced* FEScene::GetEntityInstanced(const std::string ID) +FEEntity* FEScene::DuplicateEntity(std::string ID, std::string NewEntityName) { if (EntityMap.find(ID) == EntityMap.end()) return nullptr; - if (EntityMap[ID]->GetType() != FE_ENTITY_INSTANCED) + return DuplicateEntity(EntityMap[ID], NewEntityName); +} + +FEEntity* FEScene::DuplicateEntity(FEEntity* SourceEntity, std::string NewEntityName) +{ + if (SourceEntity == nullptr) return nullptr; - return reinterpret_cast(EntityMap[ID]); + if (NewEntityName.empty()) + NewEntityName = SourceEntity->GetName() + "_Copy"; + + return DuplicateEntityInternal(SourceEntity, NewEntityName); } -std::vector FEScene::GetEntityInstancedByName(const std::string Name) +FEEntity* FEScene::DuplicateEntityInternal(FEEntity* SourceEntity, std::string NewEntityName) { - std::vector result; + FEEntity* NewEntity = CreateEntityOrphan(NewEntityName); - auto it = EntityMap.begin(); - while (it != EntityMap.end()) + std::vector List = SourceEntity->GetComponentsInfoList(); + // Sort to make sure that components are loaded in proper order. + COMPONENTS_TOOL.SortComponentsByLoadingPriority(List); + + for (size_t i = 0; i < List.size(); i++) { - if (it->second->GetType() != FE_ENTITY_INSTANCED) - { - it++; - continue; - } + if (List[i].DuplicateComponent != nullptr) + List[i].DuplicateComponent(SourceEntity, NewEntity); + } - if (it->second->GetName() == Name) - { - result.push_back(reinterpret_cast(it->second)); - } + return NewEntity; +} + +FEEntity* FEScene::ImportEntity(FEEntity* EntityFromDifferentScene, FENaiveSceneGraphNode* TargetParent, std::function Filter) +{ + FEEntity* Result = nullptr; + if (EntityFromDifferentScene == nullptr) + { + LOG.Add("EntityFromDifferentScene is nullptr in FEScene::ImportEntity", "FE_LOG_ECS", FE_LOG_ERROR); + return Result; + } + + if (EntityFromDifferentScene->GetParentScene() == this) + { + LOG.Add("EntityFromDifferentScene is already in this scene in FEScene::ImportEntity", "FE_LOG_ECS", FE_LOG_WARNING); + return Result; + } + + if (TargetParent == nullptr) + TargetParent = SceneGraph.GetRoot(); - it++; + FENaiveSceneGraphNode* OriginalNode = EntityFromDifferentScene->GetParentScene()->SceneGraph.GetNodeByEntityID(EntityFromDifferentScene->GetObjectID()); + FENaiveSceneGraphNode* NewNode = SceneGraph.ImportNode(OriginalNode, TargetParent, Filter); + if (NewNode == nullptr) + { + LOG.Add("Failed to import node in FEScene::ImportEntity", "FE_LOG_ECS", FE_LOG_ERROR); + return Result; } - return result; + Result = NewNode->GetEntity(); + return Result; } -void FEScene::SetSelectMode(FEEntityInstanced* EntityInstanced, const bool NewValue) +FEAABB FEScene::GetEntityAABB(std::string ID) { - auto it = EntityMap.begin(); - while (it != EntityMap.end()) + if (EntityMap.find(ID) == EntityMap.end()) { - if (it->second->GetType() != FE_ENTITY_INSTANCED) - { - it++; - continue; - } - - reinterpret_cast(it->second)->SetSelectMode(false); - it++; + LOG.Add("Entity with ID: " + ID + " not found in FEScene::GetEntityAABB", "FE_LOG_ECS", FE_LOG_WARNING); + return FEAABB(); } - EntityInstanced->SetSelectMode(NewValue); + return GetEntityAABB(EntityMap[ID]); } -void FEScene::DeleteLight(const std::string ID) +FEAABB FEScene::GetEntityAABB(FEEntity* Entity) { - if (OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].find(ID) != OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].end()) + FEAABB Result; + + if (Entity == nullptr) { - OBJECT_MANAGER.ObjectsByType[FE_DIRECTIONAL_LIGHT].erase(ID); - return; + LOG.Add("Call of FEScene::GetEntityAABB with nullptr", "FE_LOG_ECS", FE_LOG_WARNING); + return Result; } - - if (OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT].find(ID) != OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT].end()) + + if (Entity->HasComponent()) { - OBJECT_MANAGER.ObjectsByType[FE_SPOT_LIGHT].erase(ID); - return; + FEGameModel* GameModel = Entity->GetComponent().GetGameModel(); + Result = GameModel->GetMesh()->GetAABB().Transform(Entity->GetComponent().GetWorldMatrix()); } - if (OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT].find(ID) != OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT].end()) + if (Entity->HasComponent()) { - OBJECT_MANAGER.ObjectsByType[FE_POINT_LIGHT].erase(ID); - return; + Result = INSTANCED_RENDERING_SYSTEM.GetAABB(Entity); + } + + if (Entity->HasComponent()) + { + Result = TERRAIN_SYSTEM.GetAABB(Entity); + } + + if (Entity->HasComponent()) + { + Result = Entity->GetComponent().GetAABB(); + } + + // If entity has no renderable components, we can have FEAABB with zero volume. + // But with position. + if (Result.GetVolume() == 0 && GEOMETRY.IsEpsilonEqual(Result.GetSize(), glm::vec3(0.0f))) + { + Result.Min = Entity->GetComponent().GetPosition(); + Result.Max = Entity->GetComponent().GetPosition(); } - return; + return Result; } -FEVirtualUIContext* FEScene::AddVirtualUIContext(int Width, int Height, FEMesh* SampleMesh, std::string Name) +FEAABB FEScene::GetSceneAABB(std::function Filter) { - FEVirtualUIContext* NewVirtualUIContext = new FEVirtualUIContext(Width, Height, SampleMesh, Name); - NewVirtualUIContext->CanvasEntity = AddEntity(NewVirtualUIContext->CanvasPrefab, Name + "_Virtual_UI_Canvas"); - NewVirtualUIContext->CanvasEntity->SetUniformLighting(true); - VirtualUIContextMap[NewVirtualUIContext->GetObjectID()] = NewVirtualUIContext; - return NewVirtualUIContext; + FEAABB Result; + SceneGraph.GetNodeAABB(Result, SceneGraph.GetRoot(), Filter); + return Result; } -FEVirtualUIContext* FEScene::GetVirtualUIContext(const std::string ID) +std::vector FEScene::GetEntityByTagComponent(std::string Tag) { - if (VirtualUIContextMap.find(ID) == VirtualUIContextMap.end()) - return nullptr; + std::vector Result; - return VirtualUIContextMap[ID]; -} + auto EntityIterator = EntityMap.begin(); + while (EntityIterator != EntityMap.end()) + { + if (EntityIterator->second->HasComponent()) + { + if (EntityIterator->second->GetComponent().GetTag() == Tag) + Result.push_back(EntityIterator->second); + } -std::vector FEScene::GetVirtualUIContextList() -{ - FE_MAP_TO_STR_VECTOR(VirtualUIContextMap) + EntityIterator++; + } + + return Result; } -void FEScene::DeleteVirtualUIContext(const std::string ID) +FEEntity* FEScene::CreateEntityFromJson(Json::Value Root) { - if (VirtualUIContextMap.find(ID) == VirtualUIContextMap.end()) - return; + std::string EntityID = Root["FEObjectData"]["ID"].asString(); + std::string EntityName = Root["FEObjectData"]["Name"].asString(); + + FEEntity* NewEntity = CreateEntity(EntityName, EntityID); + NewEntity->FromJson(Root); - const FEVirtualUIContext* VirtualUIContextToDelete = VirtualUIContextMap[ID]; - delete VirtualUIContextToDelete; - VirtualUIContextMap.erase(ID); + return NewEntity; } \ No newline at end of file diff --git a/SubSystems/Scene/FEScene.h b/SubSystems/Scene/FEScene.h index e5c5055..fcd5db2 100644 --- a/SubSystems/Scene/FEScene.h +++ b/SubSystems/Scene/FEScene.h @@ -1,62 +1,121 @@ #pragma once +#include"FEEntity.h" #include "../ResourceManager/FEResourceManager.h" -#include "../CoreExtensions/FEFreeCamera.h" -#include "../CoreExtensions/FEModelViewCamera.h" -#include "../Renderer/FELight.h" #include "../Renderer/FELine.h" -#include "../FEVirtualUIContext.h" +#include "FENaiveSceneGraph.h" +#include +#include "entt.hpp" namespace FocalEngine { - class FEScene + enum class FESceneFlag : uint32_t { + None = 0, + Renderable = 1 << 0, + Active = 1 << 1, + EditorMode = 1 << 2, + GameMode = 1 << 3, + PrefabDescription = 1 << 4, + }; + + // Enable bitwise operations for FESceneFlag + inline FESceneFlag operator|(FESceneFlag First, FESceneFlag Second) + { + return static_cast( + static_cast>(First) | + static_cast>(Second) + ); + } + + inline FESceneFlag& operator|=(FESceneFlag& First, FESceneFlag Second) + { + First = First | Second; + return First; + } + + inline FESceneFlag operator&(FESceneFlag First, FESceneFlag Second) + { + return static_cast( + static_cast>(First) & + static_cast>(Second) + ); + } + + inline FESceneFlag& operator&=(FESceneFlag& First, FESceneFlag Second) + { + First = First & Second; + return First; + } + + class FEScene : public FEObject + { + friend class FEEntity; + friend class FENaiveSceneGraphNode; + friend class FENaiveSceneGraph; + friend class FESceneManager; + friend class FEInstancedSystem; friend class FERenderer; friend class FEngine; + friend class FETransformSystem; public: - SINGLETON_PUBLIC_PART(FEScene) + FEScene(); - FEEntity* AddEntity(FEGameModel* GameModel, std::string Name = "", std::string ForceObjectID = ""); - FEEntity* AddEntity(FEPrefab* Prefab, std::string Name = "", std::string ForceObjectID = ""); - bool AddEntity(FEEntity* NewEntity); + void SetFlag(FESceneFlag Flag, bool Value); + bool HasFlag(FESceneFlag Flag) const; + + // Entity Management FEEntity* GetEntity(std::string ID); + std::vector GetEntityIDList(); std::vector GetEntityByName(std::string Name); - std::vector GetEntityList(); - void DeleteEntity(std::string ID); - - FEEntityInstanced* AddEntityInstanced(FEPrefab* Prefab, std::string Name = "", std::string ForceObjectID = ""); - FEEntityInstanced* AddEntityInstanced(FEGameModel* GameModel, std::string Name = "", std::string ForceObjectID = ""); - bool AddEntityInstanced(FEEntityInstanced* NewEntityInstanced); - FEEntityInstanced* GetEntityInstanced(std::string ID); - std::vector GetEntityInstancedByName(std::string Name); - void SetSelectMode(FEEntityInstanced* EntityInstanced, bool NewValue); + std::vector GetEntityByTagComponent(std::string Tag); + template std::vector GetEntityIDListWithComponent(); + template std::vector GetEntityListWithComponent(); - FELight* AddLight(FE_OBJECT_TYPE LightType, std::string Name, std::string ForceObjectID = ""); - FELight* GetLight(std::string ID); - std::vector GetLightsList(); - void DeleteLight(std::string ID); + FEEntity* CreateEntity(std::string Name = "", std::string ForceObjectID = ""); + FEEntity* CreateEntityOrphan(std::string Name = "", std::string ForceObjectID = ""); + FEEntity* CreateEntityFromJson(Json::Value Root); + + FEEntity* DuplicateEntity(std::string ID, std::string NewEntityName = ""); + FEEntity* DuplicateEntity(FEEntity* SourceEntity, std::string NewEntityName = ""); + FEEntity* ImportEntity(FEEntity* EntityFromDifferentScene, FENaiveSceneGraphNode* TargetParent = nullptr, std::function Filter = nullptr); + void DeleteEntity(std::string ID); + void DeleteEntity(FEEntity* Entity); - bool AddTerrain(FETerrain* NewTerrain); - std::vector GetTerrainList(); - FETerrain* GetTerrain(std::string ID); - void DeleteTerrain(std::string ID); + FEAABB GetEntityAABB(std::string ID); + FEAABB GetEntityAABB(FEEntity* Entity); + FEAABB GetSceneAABB(std::function Filter = nullptr); - FEVirtualUIContext* AddVirtualUIContext(int Width = 1280, int Height = 720, FEMesh* SampleMesh = nullptr, std::string Name = "UnNamed"); - FEVirtualUIContext* GetVirtualUIContext(std::string ID); - std::vector GetVirtualUIContextList(); - void DeleteVirtualUIContext(std::string ID); + // Asset Management + std::vector ImportAsset(std::string FileName); + // Scene Management void PrepareForGameModelDeletion(const FEGameModel* GameModel); void PrepareForPrefabDeletion(const FEPrefab* Prefab); - void Clear(); + void Update(); + + FENaiveSceneGraph SceneGraph; private: - SINGLETON_PRIVATE_PART(FEScene) + ~FEScene(); + + FESceneFlag Flags = FESceneFlag::None; + + // Internal Entity Management + FEEntity* GetEntityByEnTT(entt::entity ID); + void ClearEntityRecords(std::string EntityID, entt::entity EnttEntity); + FEEntity* CreateEntityInternal(std::string Name = "", std::string ForceObjectID = ""); + FEEntity* DuplicateEntityInternal(FEEntity* SourceEntity, std::string NewEntityName = ""); + // Asset Loading + std::vector LoadGLTF(std::string FileName); + std::vector AddGLTFNodeToSceneGraph(const FEGLTFLoader& GLTF, const GLTFNodes& Node, const std::unordered_map>& GLTFMeshesToGameModelMap, const std::string ParentID); + + // Data Members + entt::registry Registry; + bool bIsSceneClearing = false; + std::unordered_map EnttToEntity; std::unordered_map EntityMap; - std::unordered_map TerrainMap; - std::unordered_map VirtualUIContextMap; }; - - #define SCENE FEScene::getInstance() +#include "FEScene.inl" } \ No newline at end of file diff --git a/SubSystems/Scene/FEScene.inl b/SubSystems/Scene/FEScene.inl new file mode 100644 index 0000000..916fc88 --- /dev/null +++ b/SubSystems/Scene/FEScene.inl @@ -0,0 +1,23 @@ +#pragma once + +template +std::vector FEScene::GetEntityIDListWithComponent() +{ + std::vector Result; + entt::basic_view ComponentView = Registry.view(); + for (entt::entity CurrentEntity : ComponentView) + Result.push_back(EnttToEntity[CurrentEntity]->GetObjectID()); + + return Result; +} + +template +std::vector FEScene::GetEntityListWithComponent() +{ + std::vector Result; + entt::basic_view ComponentView = Registry.view(); + for (entt::entity CurrentEntity : ComponentView) + Result.push_back(EnttToEntity[CurrentEntity]); + + return Result; +} \ No newline at end of file diff --git a/SubSystems/Scene/FESceneManager.cpp b/SubSystems/Scene/FESceneManager.cpp new file mode 100644 index 0000000..6141eca --- /dev/null +++ b/SubSystems/Scene/FESceneManager.cpp @@ -0,0 +1,475 @@ +#include "FESceneManager.h" +using namespace FocalEngine; + +#ifdef FOCAL_ENGINE_SHARED +extern "C" __declspec(dllexport) void* GetSceneManager() +{ + return FESceneManager::GetInstancePointer(); +} +#endif + +FESceneManager::FESceneManager() +{ +} + +FEScene* FESceneManager::GetScene(std::string ID) +{ + if (Scenes.find(ID) == Scenes.end()) + return nullptr; + + return Scenes[ID]; +} + +FEScene* FESceneManager::CreateScene(std::string Name, std::string ForceObjectID, FESceneFlag Flags) +{ + FEScene* Scene = new FEScene(); + Scene->Flags = Flags; + + if (!Name.empty()) + Scene->SetName(Name); + + if (!ForceObjectID.empty()) + Scene->SetID(ForceObjectID); + + Scenes[Scene->GetObjectID()] = Scene; + if (Scene->HasFlag(FESceneFlag::Active)) + RegisterAllComponentCallbacks(Scene); + + return Scene; +} + +Json::Value FESceneManager::SaveSceneToJSON(FEScene* Scene, std::function Filter) +{ + if (Scene == nullptr) + { + LOG.Add("FESceneManager::SaveSceneToJSON: Scene is nullptr.", "FE_LOG_ECS", FE_LOG_ERROR); + return Json::Value(); + } + + Json::Value Root; + + Root["FEObjectData"] = RESOURCE_MANAGER.SaveFEObjectPart(Scene); + Json::Value SceneHierarchy = Scene->SceneGraph.ToJson(Filter); + Root["Scene hierarchy"] = SceneHierarchy; + + return Root; +} + +FEScene* FESceneManager::LoadSceneFromJSON(Json::Value& Root, FESceneFlag Flags) +{ + FEObjectLoadedData LoadedObjectData = RESOURCE_MANAGER.LoadFEObjectPart(Root["FEObjectData"]); + + FEScene* NewScene = SCENE_MANAGER.CreateScene(LoadedObjectData.Name, LoadedObjectData.ID, Flags); + RESOURCE_MANAGER.SetTag(NewScene, LoadedObjectData.Tag); + + NewScene->SceneGraph.FromJson(Root["Scene hierarchy"]); + return NewScene; +} + +void FESceneManager::RegisterAllComponentCallbacks(FEScene* Scene) +{ + Scene->Registry.on_construct().connect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().connect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().connect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().connect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().connect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().connect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().connect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().connect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().connect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().connect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().connect<&FESceneManager::OnComponentConstructWrapper>(); + + Scene->Registry.on_destroy().connect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().connect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().connect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().connect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().connect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().connect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().connect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().connect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().connect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().connect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().connect<&FESceneManager::OnComponentDestroyWrapper>(); + + Scene->Registry.on_update().connect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().connect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().connect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().connect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().connect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().connect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().connect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().connect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().connect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().connect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().connect<&FESceneManager::OnComponentUpdateWrapper>(); +} + +void FESceneManager::UnRegisterAllComponentCallbacks(FEScene* Scene) +{ + Scene->Registry.on_construct().disconnect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().disconnect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().disconnect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().disconnect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().disconnect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().disconnect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().disconnect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().disconnect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().disconnect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().disconnect<&FESceneManager::OnComponentConstructWrapper>(); + Scene->Registry.on_construct().disconnect<&FESceneManager::OnComponentConstructWrapper>(); + + Scene->Registry.on_destroy().disconnect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().disconnect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().disconnect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().disconnect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().disconnect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().disconnect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().disconnect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().disconnect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().disconnect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().disconnect<&FESceneManager::OnComponentDestroyWrapper>(); + Scene->Registry.on_destroy().disconnect<&FESceneManager::OnComponentDestroyWrapper>(); + + Scene->Registry.on_update().disconnect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().disconnect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().disconnect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().disconnect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().disconnect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().disconnect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().disconnect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().disconnect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().disconnect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().disconnect<&FESceneManager::OnComponentUpdateWrapper>(); + Scene->Registry.on_update().disconnect<&FESceneManager::OnComponentUpdateWrapper>(); +} + +std::vector FESceneManager::GetSceneIDList() +{ + FE_MAP_TO_STR_VECTOR(Scenes) +} + +std::vector FESceneManager::GetSceneByName(const std::string Name) +{ + std::vector Result; + + auto SceneIterator = Scenes.begin(); + while (SceneIterator != Scenes.end()) + { + if (SceneIterator->second->GetName() == Name) + Result.push_back(SceneIterator->second); + + SceneIterator++; + } + + return Result; +} + +void FESceneManager::DeleteScene(std::string ID) +{ + if (Scenes.find(ID) == Scenes.end()) + return; + + DeleteScene(Scenes[ID]); +} + +void FESceneManager::DeleteScene(FEScene* Scene) +{ + if (Scene == nullptr) + return; + + std::string SceneID = Scene->GetObjectID(); + delete Scene; + + Scenes.erase(SceneID); +} + +void FESceneManager::Update() +{ + std::vector ActiveScenes = GetScenesByFlagMask(FESceneFlag::Active); + for (size_t i = 0; i < ActiveScenes.size(); i++) + { + ActiveScenes[i]->Update(); + } +} + +std::vector FESceneManager::GetAllScenes() +{ + std::vector Result; + + auto SceneIterator = Scenes.begin(); + while (SceneIterator != Scenes.end()) + { + Result.push_back(SceneIterator->second); + SceneIterator++; + } + + return Result; +} + +std::vector FESceneManager::GetScenesByFlagMask(FESceneFlag FlagMask) +{ + std::vector Result; + + auto SceneIterator = Scenes.begin(); + while (SceneIterator != Scenes.end()) + { + if ((SceneIterator->second->Flags & FlagMask) == FlagMask) + Result.push_back(SceneIterator->second); + + SceneIterator++; + } + + return Result; +} + +FEScene* FESceneManager::DuplicateScene(std::string ID, std::string NewSceneName, std::function Filter, FESceneFlag Flags) +{ + FEScene* SceneToDuplicate = GetScene(ID); + if (SceneToDuplicate == nullptr) + return nullptr; + + return DuplicateScene(SceneToDuplicate, NewSceneName, Filter, Flags); +} + +FEScene* FESceneManager::DuplicateScene(FEScene* SourceScene, std::string NewSceneName, std::function Filter, FESceneFlag Flags) +{ + FEScene* Result = CreateScene(NewSceneName, "", Flags); + + // Get children of the root entity and import them. + std::vector RootChildrens = SourceScene->SceneGraph.GetRoot()->GetChildren(); + for (auto RootChildren : RootChildrens) + { + FEEntity* EntityToDuplicate = RootChildren->GetEntity(); + Result->ImportEntity(EntityToDuplicate, nullptr, Filter); + } + + return Result; +} + +// TODO: Improve FETransformSystem robustness by reducing special cases. +#include "Components/Systems/FETransformSystem.h" +std::vector FESceneManager::ImportSceneAsNode(FEScene* SourceScene, FEScene* TargetScene, FENaiveSceneGraphNode* TargetParent, std::function Filter) +{ + std::vector Result; + if (SourceScene == nullptr || TargetScene == nullptr) + { + LOG.Add("FESceneManager::ImportSceneAsNode: SourceScene or TargetScene is nullptr.", "FE_LOG_ECS", FE_LOG_ERROR); + return Result; + } + + // We are doing this to make sure that all entities have updated world matrices. + // It is especially important for FEPrefab scenes, because they are not updated by default. + TRANSFORM_SYSTEM.UpdateInternal(SourceScene->SceneGraph.GetRoot()); + + // Get children of the root entity and import them. + std::vector RootChildrens = SourceScene->SceneGraph.GetRoot()->GetChildren(); + for (auto RootChildren : RootChildrens) + { + FEEntity* EntityToDuplicate = RootChildren->GetEntity(); + FEEntity* NewChildEntity = TargetScene->ImportEntity(EntityToDuplicate, TargetParent, Filter); + if (NewChildEntity != nullptr) + { + FENaiveSceneGraphNode* NewChildNode = TargetScene->SceneGraph.GetNodeByEntityID(NewChildEntity->GetObjectID()); + if (NewChildNode != nullptr) + { + Result.push_back(NewChildNode); + } + else + { + LOG.Add("FESceneManager::ImportSceneAsNode: Failed to get new node.", "FE_LOG_ECS", FE_LOG_ERROR); + } + } + } + + return Result; +} + +bool FESceneManager::AreSceneGraphHierarchiesEquivalent(FENaiveSceneGraphNode* FirstStaringNode, FENaiveSceneGraphNode* SecondStartingNode, bool bCheckNames) +{ + if (FirstStaringNode == nullptr || SecondStartingNode == nullptr) + return FirstStaringNode == SecondStartingNode; // Both null is considered equivalent + + if (FirstStaringNode->GetChildren().size() != SecondStartingNode->GetChildren().size()) + return false; + + if (bCheckNames && FirstStaringNode->GetName() != SecondStartingNode->GetName()) + return false; + + for (size_t i = 0; i < FirstStaringNode->GetChildren().size(); i++) + { + if (!AreSceneGraphHierarchiesEquivalent(FirstStaringNode->GetChildren()[i], SecondStartingNode->GetChildren()[i], bCheckNames)) + return false; + } + + return true; +} + +std::vector FESceneManager::InstantiatePrefab(FEPrefab* Prefab, FEScene* Scene, bool bAddToSceneRoot) +{ + std::vector Result; + + if (Prefab == nullptr || Scene == nullptr) + { + LOG.Add("FESceneManager::InstantiatePrefab: Prefab or Scene is nullptr.", "FE_LOG_ECS", FE_LOG_ERROR); + return Result; + } + + FEScene* PrefabScene = Prefab->GetScene(); + if (PrefabScene == nullptr) + { + LOG.Add("FESceneManager::InstantiatePrefab: Prefab scene is nullptr.", "FE_LOG_ECS", FE_LOG_ERROR); + return Result; + } + + FENaiveSceneGraphNode* RootNode = PrefabScene->SceneGraph.GetRoot(); + if (RootNode == nullptr) + { + LOG.Add("FESceneManager::InstantiatePrefab: Prefab scene root node is nullptr.", "FE_LOG_ECS", FE_LOG_ERROR); + return Result; + } + + if (bAddToSceneRoot) + { + // Import prefab scene graph nodes to the scene root. + std::vector PrefabNodes = ImportSceneAsNode(PrefabScene, Scene, Scene->SceneGraph.GetRoot()); + if (!PrefabNodes.empty()) + { + FEEntity* SceneRootEntity = Scene->SceneGraph.GetRoot()->GetEntity(); + for (size_t i = 0; i < PrefabNodes.size(); i++) + { + Result.push_back(PrefabNodes[i]->GetEntity()); + } + + return Result; + } + else + { + LOG.Add("FESceneManager::InstantiatePrefab: Failed to import prefab scene graph nodes.", "FE_LOG_ECS", FE_LOG_ERROR); + } + } + else + { + // Create a new entity to hold the prefab instance and its scene graph nodes + FEEntity* NewEntity = Scene->CreateEntity(Prefab->GetName()); + FENaiveSceneGraphNode* NewNode = Scene->SceneGraph.GetNodeByEntityID(NewEntity->GetObjectID()); + + std::vector PrefabNodes = ImportSceneAsNode(PrefabScene, Scene, NewNode); + if (!PrefabNodes.empty()) + { + NewEntity->AddComponent(Prefab); + Result.push_back(NewEntity); + for (size_t i = 0; i < PrefabNodes.size(); i++) + { + Result.push_back(PrefabNodes[i]->GetEntity()); + } + + return Result; + } + else + { + LOG.Add("FESceneManager::InstantiatePrefab: Failed to import prefab scene graph nodes.", "FE_LOG_ECS", FE_LOG_ERROR); + Scene->DeleteEntity(NewEntity); + } + } + + return Result; +} + +void FESceneManager::Clear() +{ + std::vector Tags = RESOURCE_MANAGER.GetTagsThatWillPreventDeletion(); + std::vector ScenesToDelete; + + auto SceneIterator = Scenes.begin(); + while (SceneIterator != Scenes.end()) + { + bool bCanDelete = true; + for (size_t i = 0; i < Tags.size(); i++) + { + if (Tags[i] == SceneIterator->second->GetTag()) + { + bCanDelete = false; + break; + } + } + + if (SceneIterator->second->GetTag() == PREFAB_SCENE_DESCRIPTION_TAG) + { + std::vector PrefabIDList = RESOURCE_MANAGER.GetPrefabIDList(); + for (size_t i = 0; i < PrefabIDList.size(); i++) + { + FEPrefab* Prefab = RESOURCE_MANAGER.GetPrefab(PrefabIDList[i]); + if (Prefab == nullptr) + continue; + + if (Prefab->GetScene() != SceneIterator->second) + continue; + + for (size_t j = 0; j < Tags.size(); j++) + { + if (Tags[j] == Prefab->GetTag()) + { + bCanDelete = false; + break; + } + } + + if (!bCanDelete) + break; + } + } + + if (bCanDelete) + ScenesToDelete.push_back(SceneIterator->second); + + SceneIterator++; + } + + for (size_t i = 0; i < ScenesToDelete.size(); i++) + { + DeleteScene(ScenesToDelete[i]); + } +} + +FEScene* FESceneManager::GetStartingScene() +{ + if (StartingSceneID.empty() || GetScene(StartingSceneID) == nullptr) + { + std::vector TagsToAvoid = RESOURCE_MANAGER.GetTagsThatWillPreventDeletion(); + TagsToAvoid.push_back(PREFAB_SCENE_DESCRIPTION_TAG); + + std::vector AllScenes = GetAllScenes(); + for (size_t i = 0; i < AllScenes.size(); i++) + { + bool bCanUse = true; + for (size_t j = 0; j < TagsToAvoid.size(); j++) + { + if (AllScenes[i]->GetTag() == TagsToAvoid[j]) + { + bCanUse = false; + break; + } + } + + if (bCanUse) + return AllScenes[i]; + } + + return nullptr; + } + + return GetScene(StartingSceneID); +} + +bool FESceneManager::SetStartingScene(std::string SceneID) +{ + if (GetScene(StartingSceneID) == nullptr) + { + LOG.Add("FESceneManager::SetStartingScene: Scene with ID " + SceneID + " does not exist.", "FE_LOG_ECS", FE_LOG_ERROR); + return false; + } + + StartingSceneID = SceneID; + return true; +} \ No newline at end of file diff --git a/SubSystems/Scene/FESceneManager.h b/SubSystems/Scene/FESceneManager.h new file mode 100644 index 0000000..f79e6ea --- /dev/null +++ b/SubSystems/Scene/FESceneManager.h @@ -0,0 +1,75 @@ +#pragma once + +#include "FEScene.h" + +namespace FocalEngine +{ + class FOCAL_ENGINE_API FESceneManager + { + friend class FERenderer; + friend class FEngine; + friend class FEEntity; + friend class FEInstancedSystem; + public: + SINGLETON_PUBLIC_PART(FESceneManager) + + FEScene* GetScene(std::string ID); + FEScene* CreateScene(std::string Name = "", std::string ForceObjectID = "", FESceneFlag Flags = FESceneFlag::None); + Json::Value SaveSceneToJSON(FEScene* Scene, std::function Filter = nullptr); + FEScene* LoadSceneFromJSON(Json::Value& Root, FESceneFlag Flags = FESceneFlag::None); + std::vector GetSceneIDList(); + std::vector GetSceneByName(std::string Name); + + void DeleteScene(std::string ID); + void DeleteScene(FEScene* Scene); + + FEScene* DuplicateScene(std::string ID, std::string NewSceneName = "", std::function Filter = nullptr, FESceneFlag Flags = FESceneFlag::None); + FEScene* DuplicateScene(FEScene* SourceScene, std::string NewSceneName = "", std::function Filter = nullptr, FESceneFlag Flags = FESceneFlag::None); + + std::vector ImportSceneAsNode(FEScene* SourceScene, FEScene* TargetScene, FENaiveSceneGraphNode* TargetParent = nullptr, std::function Filter = nullptr); + + template + void RegisterOnComponentConstructCallback(std::function Callback); + template + void RegisterOnComponentDestroyCallback(std::function Callback); + template + void RegisterOnComponentUpdateCallback(std::function Callback); + + void Update(); + + void Clear(); + + std::vector GetAllScenes(); + std::vector GetScenesByFlagMask(FESceneFlag FlagMask); + + void RegisterAllComponentCallbacks(FEScene* Scene); + void UnRegisterAllComponentCallbacks(FEScene* Scene); + + // Checks if two scene graph hierarchies are equivalent. + // It will not check entities, only the hierarchy structure. + bool AreSceneGraphHierarchiesEquivalent(FENaiveSceneGraphNode* FirstStaringNode, FENaiveSceneGraphNode* SecondStartingNode, bool bCheckNames = false); + + std::vector InstantiatePrefab(FEPrefab* Prefab, FEScene* Scene, bool bAddToSceneRoot = false); + + bool SetStartingScene(std::string SceneID); + FEScene* GetStartingScene(); + private: + SINGLETON_PRIVATE_PART(FESceneManager) + + std::unordered_map Scenes; + std::string StartingSceneID; + + template + static void OnComponentConstructWrapper(entt::registry& Registry, entt::entity EnTTEntity); + std::unordered_map> OnComponentConstructCallbacks; + + template + static void OnComponentDestroyWrapper(entt::registry& Registry, entt::entity EnTTEntity); + std::unordered_map> OnComponentDestroyCallbacks; + + template + static void OnComponentUpdateWrapper(entt::registry& Registry, entt::entity EnTTEntity); + std::unordered_map> OnComponentUpdateCallbacks; + }; +#include "FESceneManager.inl" +} \ No newline at end of file diff --git a/SubSystems/Scene/FESceneManager.inl b/SubSystems/Scene/FESceneManager.inl new file mode 100644 index 0000000..d91fefd --- /dev/null +++ b/SubSystems/Scene/FESceneManager.inl @@ -0,0 +1,98 @@ +#pragma once + +#ifdef FOCAL_ENGINE_SHARED + extern "C" __declspec(dllexport) void* GetSceneManager(); + #define SCENE_MANAGER (*static_cast(GetSceneManager())) +#else + #define SCENE_MANAGER FESceneManager::GetInstance() +#endif + +template +void FESceneManager::RegisterOnComponentConstructCallback(std::function Callback) +{ + // Only one callback per component type is allowed. + if (OnComponentConstructCallbacks.find(std::type_index(typeid(T))) == OnComponentConstructCallbacks.end()) + OnComponentConstructCallbacks[std::type_index(typeid(T))] = std::move(Callback); +} + +template +void FESceneManager::RegisterOnComponentDestroyCallback(std::function Callback) +{ + // Only one callback per component type is allowed. + if (OnComponentDestroyCallbacks.find(std::type_index(typeid(T))) == OnComponentDestroyCallbacks.end()) + OnComponentDestroyCallbacks[std::type_index(typeid(T))] = std::move(Callback); +} + +template +void FESceneManager::RegisterOnComponentUpdateCallback(std::function Callback) +{ + // Only one callback per component type is allowed. + if (OnComponentUpdateCallbacks.find(std::type_index(typeid(T))) == OnComponentUpdateCallbacks.end()) + OnComponentUpdateCallbacks[std::type_index(typeid(T))] = std::move(Callback); +} + +template +static void FESceneManager::OnComponentConstructWrapper(entt::registry& Registry, entt::entity EnTTEntity) +{ + entt::registry* RegistryPointer = &Registry; + std::vector ActiveScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active); + for (size_t i = 0; i < ActiveScenes.size(); i++) + { + if (&ActiveScenes[i]->Registry == RegistryPointer) + { + FEEntity* Entity = ActiveScenes[i]->GetEntityByEnTT(EnTTEntity); + if (Entity != nullptr) + { + auto CallBackMapIterator = SCENE_MANAGER.OnComponentConstructCallbacks.find(std::type_index(typeid(T))); + if (CallBackMapIterator != SCENE_MANAGER.OnComponentConstructCallbacks.end()) + CallBackMapIterator->second(Entity); + + break; + } + } + } +} + +template +static void FESceneManager::OnComponentDestroyWrapper(entt::registry& Registry, entt::entity EnTTEntity) +{ + entt::registry* RegistryPointer = &Registry; + std::vector ActiveScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active); + for (size_t i = 0; i < ActiveScenes.size(); i++) + { + if (&ActiveScenes[i]->Registry == RegistryPointer) + { + FEEntity* Entity = ActiveScenes[i]->GetEntityByEnTT(EnTTEntity); + if (Entity != nullptr) + { + auto CallBackMapIterator = SCENE_MANAGER.OnComponentDestroyCallbacks.find(std::type_index(typeid(T))); + if (CallBackMapIterator != SCENE_MANAGER.OnComponentDestroyCallbacks.end()) + CallBackMapIterator->second(Entity, ActiveScenes[i]->bIsSceneClearing); + + break; + } + } + } +} + +template +static void FESceneManager::OnComponentUpdateWrapper(entt::registry& Registry, entt::entity EnTTEntity) +{ + entt::registry* RegistryPointer = &Registry; + std::vector ActiveScenes = SCENE_MANAGER.GetScenesByFlagMask(FESceneFlag::Active); + for (size_t i = 0; i < ActiveScenes.size(); i++) + { + if (&ActiveScenes[i]->Registry == RegistryPointer) + { + FEEntity* Entity = ActiveScenes[i]->GetEntityByEnTT(EnTTEntity); + if (Entity != nullptr) + { + auto CallBackMapIterator = SCENE_MANAGER.OnComponentUpdateCallbacks.find(std::type_index(typeid(T))); + if (CallBackMapIterator != SCENE_MANAGER.OnComponentUpdateCallbacks.end()) + CallBackMapIterator->second(Entity); + + break; + } + } + } +} \ No newline at end of file diff --git a/SubSystems/Scene/FETransformComponent.cpp b/SubSystems/Scene/FETransformComponent.cpp deleted file mode 100644 index 859e888..0000000 --- a/SubSystems/Scene/FETransformComponent.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include "FETransformComponent.h" -using namespace FocalEngine; - -FETransformComponent::FETransformComponent() -{ - Position = glm::vec3(0.0f); - RotationAngles = glm::vec3(0.0f); - RotationQuaternion = glm::quat(1.0f, glm::vec3(0.0f)); - Scale = glm::vec3(1.0f, 1.0f, 1.0f); - - Update(); -} - -FETransformComponent::FETransformComponent(glm::mat4 Matrix) -{ - Position = glm::vec3(Matrix[3][0], Matrix[3][1], Matrix[3][2]); - Scale = glm::vec3(glm::length(Matrix[0]), glm::length(Matrix[1]), glm::length(Matrix[2])); - - Matrix[3][0] = 0.0f; - Matrix[3][1] = 0.0f; - Matrix[3][2] = 0.0f; - - Matrix[0] /= Scale.x; - Matrix[1] /= Scale.y; - Matrix[2] /= Scale.z; - - RotationQuaternion = glm::quat_cast(Matrix); - RotationAngles = glm::eulerAngles(RotationQuaternion) * 180.0f / glm::pi(); - - Update(); -} - -FETransformComponent::~FETransformComponent() -{ -} - -glm::vec3 FETransformComponent::GetPosition() const -{ - return Position; -} - -glm::vec3 FETransformComponent::GetRotation() const -{ - return RotationAngles; -} - -glm::vec3 FETransformComponent::GetScale() const -{ - return Scale; -} - -void FETransformComponent::SetPosition(const glm::vec3 NewPosition) -{ - Position = NewPosition; - Update(); -} - -void FETransformComponent::RotateQuaternion(const float Angle, const glm::vec3 Axis) -{ - RotationQuaternion = glm::quat(cos(Angle / 2), - Axis.x * sin(Angle / 2), - Axis.y * sin(Angle / 2), - Axis.z * sin(Angle / 2)) * RotationQuaternion; -} - -void FETransformComponent::SetRotation(const glm::vec3 NewRotation) -{ - if (RotationAngles == NewRotation) - return; - - RotationQuaternion = glm::quat(1.0f, glm::vec3(0.0f)); - RotateQuaternion(static_cast(NewRotation.x) * ANGLE_TORADIANS_COF, glm::vec3(1, 0, 0)); - RotateQuaternion(static_cast(NewRotation.y) * ANGLE_TORADIANS_COF, glm::vec3(0, 1, 0)); - RotateQuaternion(static_cast(NewRotation.z) * ANGLE_TORADIANS_COF, glm::vec3(0, 0, 1)); - - RotationAngles = NewRotation; - Update(); -} - -void FETransformComponent::RotateByQuaternion(const glm::quat Quaternion) -{ - RotationQuaternion = Quaternion * RotationQuaternion; - glm::vec3 NewRotationAngle = glm::eulerAngles(GetQuaternion()); - - NewRotationAngle.x /= ANGLE_TORADIANS_COF; - NewRotationAngle.y /= ANGLE_TORADIANS_COF; - NewRotationAngle.z /= ANGLE_TORADIANS_COF; - - RotationAngles = NewRotationAngle; - Update(); -} - -void FETransformComponent::ChangeScaleUniformlyBy(const float Delta) -{ - Scale += Delta; - Update(); -} - -void FETransformComponent::ChangeXScaleBy(const float Delta) -{ - if (bUniformScaling) - { - ChangeScaleUniformlyBy(Delta); - } - else - { - Scale[0] += Delta; - } - - Update(); -} - -void FETransformComponent::ChangeYScaleBy(const float Delta) -{ - if (bUniformScaling) - { - ChangeScaleUniformlyBy(Delta); - } - else - { - Scale[1] += Delta; - } - - Update(); -} - -void FETransformComponent::ChangeZScaleBy(const float Delta) -{ - if (bUniformScaling) - { - ChangeScaleUniformlyBy(Delta); - } - else - { - Scale[2] += Delta; - } - - Update(); -} - -void FETransformComponent::SetScale(const glm::vec3 NewScale) -{ - Scale = NewScale; - Update(); -} - -void FETransformComponent::Update() -{ - TransformMatrix = glm::mat4(1.0); - TransformMatrix = glm::translate(TransformMatrix, Position); - - TransformMatrix *= glm::toMat4(RotationQuaternion); - TransformMatrix = glm::scale(TransformMatrix, glm::vec3(Scale[0], Scale[1], Scale[2])); - - if (PreviousTransformMatrix != TransformMatrix) - bDirtyFlag = true; - PreviousTransformMatrix = TransformMatrix; -} - -glm::mat4 FETransformComponent::GetTransformMatrix() const -{ - return TransformMatrix; -} - -glm::quat FETransformComponent::GetQuaternion() const -{ - return RotationQuaternion; -} - -bool FETransformComponent::IsDirty() const -{ - return bDirtyFlag; -} - -void FETransformComponent::SetDirtyFlag(const bool NewValue) -{ - bDirtyFlag = NewValue; -} - -void FETransformComponent::ForceSetTransformMatrix(const glm::mat4 NewValue) -{ - TransformMatrix = NewValue; -} - -FETransformComponent FETransformComponent::Combine(const FETransformComponent& Other) const -{ - FETransformComponent result; - - result.SetPosition(GetPosition() + Other.GetPosition()); - result.SetRotation(GetRotation() + Other.GetRotation()); - result.SetScale(GetScale() * Other.GetScale()); - - return result; -} - -bool FETransformComponent::IsUniformScalingSet() const -{ - return bUniformScaling; -} - -void FETransformComponent::SetUniformScaling(const bool NewValue) -{ - bUniformScaling = NewValue; -} - -void FETransformComponent::SetQuaternion(glm::quat Quaternion) -{ - RotationQuaternion = Quaternion; - glm::vec3 NewRotationAngle = glm::eulerAngles(GetQuaternion()); - - NewRotationAngle.x /= ANGLE_TORADIANS_COF; - NewRotationAngle.y /= ANGLE_TORADIANS_COF; - NewRotationAngle.z /= ANGLE_TORADIANS_COF; - - RotationAngles = NewRotationAngle; - Update(); -} \ No newline at end of file diff --git a/SubSystems/Scene/FETransformComponent.h b/SubSystems/Scene/FETransformComponent.h deleted file mode 100644 index 3735854..0000000 --- a/SubSystems/Scene/FETransformComponent.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -#include "../Core/FEObject.h" - -namespace FocalEngine -{ - class FEEntity; - class FEEntityInstanced; - class FETerrain; - class FERenderer; - - class FETransformComponent - { - friend FEEntity; - friend FEEntityInstanced; - friend FETerrain; - friend FERenderer; - public: - FETransformComponent(); - FETransformComponent(glm::mat4 Matrix); - ~FETransformComponent(); - - FETransformComponent Combine(const FETransformComponent& Other) const; - - glm::vec3 GetPosition() const; - glm::vec3 GetRotation() const; - glm::quat GetQuaternion() const; - glm::vec3 GetScale() const; - - void SetPosition(glm::vec3 NewPosition); - void SetRotation(glm::vec3 NewRotation); - void SetQuaternion(glm::quat Quaternion); - void RotateByQuaternion(glm::quat Quaternion); - void SetScale(glm::vec3 NewScale); - - void ChangeScaleUniformlyBy(float Delta); - void ChangeXScaleBy(float Delta); - void ChangeYScaleBy(float Delta); - void ChangeZScaleBy(float Delta); - - glm::mat4 GetTransformMatrix() const; - void ForceSetTransformMatrix(glm::mat4 NewValue); - void Update(); - - bool IsDirty() const; - void SetDirtyFlag(bool NewValue); - - bool IsUniformScalingSet() const; - void SetUniformScaling(bool NewValue); - private: - bool bDirtyFlag = false; - glm::vec3 Position; - glm::quat RotationQuaternion; - glm::vec3 RotationAngles; - bool bUniformScaling = true; - - glm::vec3 Scale; - - glm::mat4 TransformMatrix; - glm::mat4 PreviousTransformMatrix; - - void RotateQuaternion(float Angle, glm::vec3 Axis); - }; -} \ No newline at end of file diff --git a/Tests/RunAllTests.cpp b/Tests/RunAllTests.cpp new file mode 100644 index 0000000..236b93b --- /dev/null +++ b/Tests/RunAllTests.cpp @@ -0,0 +1,24 @@ +#include "RunAllTests.h" +using namespace FocalEngine; + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + // Initialize Google Test + testing::GTEST_FLAG(output) = "xml:report.xml"; + testing::InitGoogleTest(); + + ENGINE.InitWindow(1280, 720); + THREAD_POOL.SetConcurrentThreadCount(10); + //LOG.SetFileOutput(true); + + int TestResult = RUN_ALL_TESTS(); + + while (ENGINE.IsNotTerminated()) + { + ENGINE.BeginFrame(); + ENGINE.Render(); + ENGINE.EndFrame(); + } + + return 0; +} \ No newline at end of file diff --git a/Tests/RunAllTests.h b/Tests/RunAllTests.h new file mode 100644 index 0000000..db3e819 --- /dev/null +++ b/Tests/RunAllTests.h @@ -0,0 +1,3 @@ +#pragma once + +#include "SceneGraphTest.h" \ No newline at end of file diff --git a/Tests/SceneGraphTest.cpp b/Tests/SceneGraphTest.cpp new file mode 100644 index 0000000..226abd7 --- /dev/null +++ b/Tests/SceneGraphTest.cpp @@ -0,0 +1,1770 @@ +#include "SceneGraphTest.h" +using namespace FocalEngine; + +// Using a fixed seed to make it easier to debug. +#define RANDOM_SEED 42 +#define RANDOM_ACTIONS_ITERATIONS 1000 + +TEST(SceneGraph, Check_Basic_Add_Find_Delete_Nodes) +{ + LOG.Add("Starting SceneGraphTest::Check_Basic_Add_Find_Delete_Nodes", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + // Get root node from the scene returns valid node. + FENaiveSceneGraphNode* RootNode = CurrentScene->SceneGraph.GetRoot(); + ASSERT_NE(RootNode, nullptr); + + // Root node can be found by its ID. + FENaiveSceneGraphNode* RootNodeByID = CurrentScene->SceneGraph.GetNode(RootNode->GetObjectID()); + ASSERT_EQ(RootNode, RootNodeByID); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + + // Root node can not be deleted. + CurrentScene->SceneGraph.DeleteNode(RootNode); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + ASSERT_NE(CurrentScene->SceneGraph.GetNode(RootNode->GetObjectID()), nullptr); + + // Add a new node to the CurrentScene-> + FEEntity* Node_A = CurrentScene->CreateEntity("Node_A"); + // Temporary using old style entities. + std::string Node_A_ID = CurrentScene->SceneGraph.GetNodeByEntityID(Node_A->GetObjectID())->GetObjectID(); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 1); + + // New node could be found by its ID. + FENaiveSceneGraphNode* NodeByID = CurrentScene->SceneGraph.GetNode(Node_A_ID); + ASSERT_NE(NodeByID, nullptr); + + // Delete the new node. + CurrentScene->SceneGraph.DeleteNode(NodeByID); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + + // Check that deleted node can not be found. + ASSERT_EQ(CurrentScene->SceneGraph.GetNode(Node_A_ID), nullptr); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Basic_Add_Find_Delete_Nodes", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST(SceneGraph, Check_For_Cycles) +{ + LOG.Add("Starting SceneGraphTest::Check_For_Cycles", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + + // Create a few nodes. + FEEntity* Node_A = CurrentScene->CreateEntity("Node_A"); + FEEntity* Node_B = CurrentScene->CreateEntity("Node_B"); + FEEntity* Node_C = CurrentScene->CreateEntity("Node_C"); + + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 3); + + // Temporary using old style entities. + std::string Node_A_ID = CurrentScene->SceneGraph.GetNodeByEntityID(Node_A->GetObjectID())->GetObjectID(); + std::string Node_B_ID = CurrentScene->SceneGraph.GetNodeByEntityID(Node_B->GetObjectID())->GetObjectID(); + std::string Node_C_ID = CurrentScene->SceneGraph.GetNodeByEntityID(Node_C->GetObjectID())->GetObjectID(); + + FENaiveSceneGraphNode* NodeA = CurrentScene->SceneGraph.GetNode(Node_A_ID); + FENaiveSceneGraphNode* NodeB = CurrentScene->SceneGraph.GetNode(Node_B_ID); + FENaiveSceneGraphNode* NodeC = CurrentScene->SceneGraph.GetNode(Node_C_ID); + + // Create a valid hierarchy. + CurrentScene->SceneGraph.MoveNode(NodeB->GetObjectID(), NodeA->GetObjectID()); + CurrentScene->SceneGraph.MoveNode(NodeC->GetObjectID(), NodeB->GetObjectID()); + + // Check that there are no cycles in this valid hierarchy. + ASSERT_FALSE(CurrentScene->SceneGraph.HasCycle(NodeA)); + ASSERT_FALSE(CurrentScene->SceneGraph.HasCycle(NodeB)); + ASSERT_FALSE(CurrentScene->SceneGraph.HasCycle(NodeC)); + + // Try to create a cycle. + ASSERT_FALSE(CurrentScene->SceneGraph.MoveNode(NodeA->GetObjectID(), NodeC->GetObjectID())); + + // Verify that the cycle was not created. + ASSERT_FALSE(CurrentScene->SceneGraph.HasCycle(NodeA)); + ASSERT_FALSE(CurrentScene->SceneGraph.HasCycle(NodeB)); + ASSERT_FALSE(CurrentScene->SceneGraph.HasCycle(NodeC)); + + // Try to create a self-cycle. + ASSERT_FALSE(CurrentScene->SceneGraph.MoveNode(NodeA->GetObjectID(), NodeA->GetObjectID())); + + // Verify that the self-cycle was not created. + ASSERT_FALSE(CurrentScene->SceneGraph.HasCycle(NodeA)); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_For_Cycles", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_GetNodeCount_AND_ChildCount_Functions) +{ + LOG.Add("Starting SceneGraphTest::Check_GetNodeCount_AND_ChildCount_Functions", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + std::vector Nodes = PopulateSceneGraphMediumSize(CurrentScene); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + + // Check root node (0) + ASSERT_EQ(Nodes[0]->GetImediateChildrenCount(), 3); + ASSERT_EQ(Nodes[0]->GetRecursiveChildCount(), 29); + + // Check level 1 nodes (1, 2, 3) + ASSERT_EQ(Nodes[1]->GetImediateChildrenCount(), 3); + ASSERT_EQ(Nodes[1]->GetRecursiveChildCount(), 8); + ASSERT_EQ(Nodes[2]->GetImediateChildrenCount(), 3); + ASSERT_EQ(Nodes[2]->GetRecursiveChildCount(), 8); + ASSERT_EQ(Nodes[3]->GetImediateChildrenCount(), 3); + ASSERT_EQ(Nodes[3]->GetRecursiveChildCount(), 10); + + // Check some level 2 nodes (4, 5, 6, 7, 8, 9, 10, 11) + ASSERT_EQ(Nodes[4]->GetImediateChildrenCount(), 2); + ASSERT_EQ(Nodes[4]->GetRecursiveChildCount(), 3); + ASSERT_EQ(Nodes[5]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[5]->GetRecursiveChildCount(), 2); + ASSERT_EQ(Nodes[6]->GetImediateChildrenCount(), 0); + ASSERT_EQ(Nodes[6]->GetRecursiveChildCount(), 0); + ASSERT_EQ(Nodes[7]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[7]->GetRecursiveChildCount(), 2); + ASSERT_EQ(Nodes[8]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[8]->GetRecursiveChildCount(), 1); + ASSERT_EQ(Nodes[9]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[9]->GetRecursiveChildCount(), 2); + ASSERT_EQ(Nodes[10]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[10]->GetRecursiveChildCount(), 1); + ASSERT_EQ(Nodes[11]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[11]->GetRecursiveChildCount(), 2); + ASSERT_EQ(Nodes[12]->GetImediateChildrenCount(), 2); + ASSERT_EQ(Nodes[12]->GetRecursiveChildCount(), 4); + + // Check some level 3 nodes (13, 14, 15, 16, 17, 18, 19, 20, 21 22) + ASSERT_EQ(Nodes[13]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[13]->GetRecursiveChildCount(), 1); + ASSERT_EQ(Nodes[14]->GetImediateChildrenCount(), 0); + ASSERT_EQ(Nodes[14]->GetRecursiveChildCount(), 0); + ASSERT_EQ(Nodes[15]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[15]->GetRecursiveChildCount(), 1); + ASSERT_EQ(Nodes[16]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[16]->GetRecursiveChildCount(), 1); + ASSERT_EQ(Nodes[17]->GetImediateChildrenCount(), 0); + ASSERT_EQ(Nodes[17]->GetRecursiveChildCount(), 0); + ASSERT_EQ(Nodes[18]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[18]->GetRecursiveChildCount(), 1); + ASSERT_EQ(Nodes[19]->GetImediateChildrenCount(), 0); + ASSERT_EQ(Nodes[19]->GetRecursiveChildCount(), 0); + ASSERT_EQ(Nodes[20]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[20]->GetRecursiveChildCount(), 1); + ASSERT_EQ(Nodes[21]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[21]->GetRecursiveChildCount(), 1); + ASSERT_EQ(Nodes[22]->GetImediateChildrenCount(), 1); + ASSERT_EQ(Nodes[22]->GetRecursiveChildCount(), 1); + + // Check some level 4 nodes (23, 24, 25, 26, 27, 28, 29) (leaf nodes) + for (size_t i = 23; i <= 29; i++) + { + ASSERT_EQ(Nodes[i]->GetImediateChildrenCount(), 0); + ASSERT_EQ(Nodes[i]->GetRecursiveChildCount(), 0); + } + + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_GetNodeCount_AND_ChildCount_Functions", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_MoveNode_Function) +{ + LOG.Add("Starting SceneGraphTest::Check_MoveNode_Function", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + std::vector Nodes = PopulateSceneGraphMediumSize(CurrentScene); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + + // First simple move + ASSERT_TRUE(CurrentScene->SceneGraph.MoveNode(Nodes[15]->GetObjectID(), Nodes[10]->GetObjectID())); + ASSERT_EQ(Nodes[5]->GetImediateChildrenCount(), 0); + ASSERT_EQ(Nodes[5]->GetRecursiveChildCount(), 0); + ASSERT_EQ(Nodes[10]->GetImediateChildrenCount(), 2); + ASSERT_EQ(Nodes[10]->GetRecursiveChildCount(), 3); + ASSERT_EQ(Nodes[3]->GetRecursiveChildCount(), 12); + ASSERT_EQ(Nodes[1]->GetRecursiveChildCount(), 6); + + srand(RANDOM_SEED); + for (size_t i = 0; i < RANDOM_ACTIONS_ITERATIONS; i++) + { + size_t RandomIndexOfNodeToMove = rand() % CurrentScene->SceneGraph.GetNodeCount(); + size_t RandomIndexOfNewParent = rand() % CurrentScene->SceneGraph.GetNodeCount(); + + size_t NodeToMoveChildrenCount = Nodes[RandomIndexOfNodeToMove]->GetImediateChildrenCount(); + size_t NodeToMoveRecursiveChildCount = Nodes[RandomIndexOfNodeToMove]->GetRecursiveChildCount(); + + size_t NewParentChildrenCount = Nodes[RandomIndexOfNewParent]->GetImediateChildrenCount(); + size_t NewParentRecursiveChildCount = Nodes[RandomIndexOfNewParent]->GetRecursiveChildCount(); + + bool bParentWasDescendant = CurrentScene->SceneGraph.IsDescendant(Nodes[RandomIndexOfNewParent], Nodes[RandomIndexOfNodeToMove]); + if (CurrentScene->SceneGraph.MoveNode(Nodes[RandomIndexOfNodeToMove]->GetObjectID(), Nodes[RandomIndexOfNewParent]->GetObjectID())) + { + // If node was moved, check if the counts are correct. + ASSERT_EQ(Nodes[RandomIndexOfNodeToMove]->GetImediateChildrenCount(), NodeToMoveChildrenCount); + ASSERT_EQ(Nodes[RandomIndexOfNodeToMove]->GetRecursiveChildCount(), NodeToMoveRecursiveChildCount); + + ASSERT_EQ(Nodes[RandomIndexOfNewParent]->GetImediateChildrenCount(), NewParentChildrenCount + 1); + if (bParentWasDescendant) + { + ASSERT_EQ(Nodes[RandomIndexOfNewParent]->GetRecursiveChildCount(), NewParentRecursiveChildCount); + } + else + { + ASSERT_EQ(Nodes[RandomIndexOfNewParent]->GetRecursiveChildCount(), NewParentRecursiveChildCount + NodeToMoveRecursiveChildCount + 1); + } + } + else + { + // If node was not moved, check if the counts are the same. + ASSERT_EQ(Nodes[RandomIndexOfNodeToMove]->GetImediateChildrenCount(), NodeToMoveChildrenCount); + ASSERT_EQ(Nodes[RandomIndexOfNodeToMove]->GetRecursiveChildCount(), NodeToMoveRecursiveChildCount); + + ASSERT_EQ(Nodes[RandomIndexOfNewParent]->GetImediateChildrenCount(), NewParentChildrenCount); + ASSERT_EQ(Nodes[RandomIndexOfNewParent]->GetRecursiveChildCount(), NewParentRecursiveChildCount); + } + + // Check if the scene graph still have same number of nodes. + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + } + + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_MoveNode_Function", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_Node_AddChild_Function) +{ + LOG.Add("Starting SceneGraphTest::Check_Node_AddChild_Function", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + std::vector Nodes = PopulateSceneGraphMediumSize(CurrentScene); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + + // Try to add a child that is nullptr + Nodes[0]->AddChild(nullptr); + ASSERT_EQ(Nodes[0]->GetImediateChildrenCount(), 3); + + // Try to add a child to it self + Nodes[0]->AddChild(Nodes[0]); + ASSERT_EQ(Nodes[0]->GetImediateChildrenCount(), 3); + + // Try to add a child that is already a child + Nodes[0]->AddChild(Nodes[1]); + ASSERT_EQ(Nodes[0]->GetImediateChildrenCount(), 3); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Node_AddChild_Function", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_Extensive_Node_Manipulation) +{ + LOG.Add("Starting SceneGraphTest::Check_Extensive_Node_Manipulation", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + std::vector Nodes = PopulateSceneGraphMediumSize(CurrentScene); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + + const size_t MaxNodes = 10000; + + srand(RANDOM_SEED); + for (size_t i = 0; i < RANDOM_ACTIONS_ITERATIONS; i++) + { + size_t RandomAction = rand() % 3; + while ((CurrentScene->SceneGraph.GetNodeCount() >= MaxNodes && RandomAction == 0) || + (CurrentScene->SceneGraph.GetNodeCount() == 0 && RandomAction != 0)) + { + RandomAction = rand() % 2; + } + size_t OldNodeCount = CurrentScene->SceneGraph.GetNodeCount(); + + // Add a new node + if (RandomAction == 0) + { + size_t AmountOfNodesToAdd = rand() % 5 + 1; + for (size_t i = 0; i < AmountOfNodesToAdd; i++) + { + FEEntity* Entity = CurrentScene->CreateEntity("Node_" + std::to_string(CurrentScene->SceneGraph.GetNodeCount())); + Nodes.push_back(CurrentScene->SceneGraph.GetNodeByEntityID(Entity->GetObjectID())); + } + + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), OldNodeCount + AmountOfNodesToAdd); + } + // Move a node + else if (RandomAction == 1) + { + size_t RandomIndexOfNodeToMove = rand() % CurrentScene->SceneGraph.GetNodeCount(); + size_t RandomIndexOfNewParent = rand() % CurrentScene->SceneGraph.GetNodeCount(); + + size_t NodeToMoveChildrenCount = Nodes[RandomIndexOfNodeToMove]->GetImediateChildrenCount(); + size_t NodeToMoveRecursiveChildCount = Nodes[RandomIndexOfNodeToMove]->GetRecursiveChildCount(); + + size_t NewParentChildrenCount = Nodes[RandomIndexOfNewParent]->GetImediateChildrenCount(); + size_t NewParentRecursiveChildCount = Nodes[RandomIndexOfNewParent]->GetRecursiveChildCount(); + + bool bParentWasDescendant = CurrentScene->SceneGraph.IsDescendant(Nodes[RandomIndexOfNewParent], Nodes[RandomIndexOfNodeToMove]); + if (CurrentScene->SceneGraph.MoveNode(Nodes[RandomIndexOfNodeToMove]->GetObjectID(), Nodes[RandomIndexOfNewParent]->GetObjectID())) + { + // If node was moved, check if the counts are correct. + ASSERT_EQ(Nodes[RandomIndexOfNodeToMove]->GetImediateChildrenCount(), NodeToMoveChildrenCount); + ASSERT_EQ(Nodes[RandomIndexOfNodeToMove]->GetRecursiveChildCount(), NodeToMoveRecursiveChildCount); + + ASSERT_EQ(Nodes[RandomIndexOfNewParent]->GetImediateChildrenCount(), NewParentChildrenCount + 1); + if (bParentWasDescendant) + { + ASSERT_EQ(Nodes[RandomIndexOfNewParent]->GetRecursiveChildCount(), NewParentRecursiveChildCount); + } + else + { + ASSERT_EQ(Nodes[RandomIndexOfNewParent]->GetRecursiveChildCount(), NewParentRecursiveChildCount + NodeToMoveRecursiveChildCount + 1); + } + } + else + { + // If node was not moved, check if the counts are the same. + ASSERT_EQ(Nodes[RandomIndexOfNodeToMove]->GetImediateChildrenCount(), NodeToMoveChildrenCount); + ASSERT_EQ(Nodes[RandomIndexOfNodeToMove]->GetRecursiveChildCount(), NodeToMoveRecursiveChildCount); + + ASSERT_EQ(Nodes[RandomIndexOfNewParent]->GetImediateChildrenCount(), NewParentChildrenCount); + ASSERT_EQ(Nodes[RandomIndexOfNewParent]->GetRecursiveChildCount(), NewParentRecursiveChildCount); + } + + // Check if the scene graph still have same number of nodes. + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), OldNodeCount); + } + // Delete a node + else if (RandomAction == 2) + { + size_t RandomIndexOfNodeToDelete = rand() % CurrentScene->SceneGraph.GetNodeCount(); + FENaiveSceneGraphNode* Parent = Nodes[RandomIndexOfNodeToDelete]->GetParent(); + size_t ParentChildrenCount = Parent->GetImediateChildrenCount(); + size_t ParentRecursiveChildCount = Parent->GetRecursiveChildCount(); + + FENaiveSceneGraphNode* NodeToDelete = Nodes[RandomIndexOfNodeToDelete]; + size_t NodeToDeleteChildrenCount = NodeToDelete->GetImediateChildrenCount(); + size_t NodeToDeleteRecursiveChildCount = NodeToDelete->GetRecursiveChildCount(); + + std::vector AllChildren = NodeToDelete->GetRecursiveChildren(); + Nodes.erase(Nodes.begin() + RandomIndexOfNodeToDelete); + for (size_t j = 0; j < AllChildren.size(); j++) + { + for (size_t k = 0; k < Nodes.size(); k++) + { + if (Nodes[k] == AllChildren[j]) + { + Nodes.erase(Nodes.begin() + k); + break; + } + } + } + ASSERT_EQ(Nodes.size(), OldNodeCount - NodeToDeleteRecursiveChildCount - 1); + + CurrentScene->SceneGraph.DeleteNode(NodeToDelete); + + // Check if the counts are correct. + ASSERT_EQ(Parent->GetImediateChildrenCount(), ParentChildrenCount - 1); + ASSERT_EQ(Parent->GetRecursiveChildCount(), ParentRecursiveChildCount - NodeToDeleteRecursiveChildCount - 1); + + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), OldNodeCount - NodeToDeleteRecursiveChildCount - 1); + } + } + + //test = CurrentScene->SceneGraph.ToJson().toStyledString(); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Extensive_Node_Manipulation", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_IsDescendant_Function) +{ + LOG.Add("Starting SceneGraphTest::Check_IsDescendant_Function", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + std::vector Nodes = PopulateSceneGraphMediumSize(CurrentScene); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + + // Test direct parent-child relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[1])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[2])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[3])); + + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[1], Nodes[4])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[1], Nodes[5])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[1], Nodes[6])); + + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[15], Nodes[24])); + + // Test grandparent relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[5])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[8])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[12])); + + // Test great-grandparent relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[2], Nodes[26])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[3], Nodes[29])); + + // Test long-distance relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[23])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[24])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[25])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[26])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[27])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[28])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[29])); + + // Test non-descendant relationships + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(Nodes[1], Nodes[25])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(Nodes[1], Nodes[7])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(Nodes[1], Nodes[3])); + + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(Nodes[2], Nodes[24])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(Nodes[2], Nodes[10])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(Nodes[2], Nodes[23])); + + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(Nodes[1], Nodes[2])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(Nodes[2], Nodes[1])); + + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(Nodes[23], Nodes[24])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(Nodes[24], Nodes[23])); + + // Test self-relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], Nodes[0])); + + // Test with null nodes + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(nullptr, Nodes[0])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(Nodes[0], nullptr)); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(nullptr, nullptr)); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_IsDescendant_Function", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_Position_Inheritance_Propagation) +{ + LOG.Add("Starting SceneGraphTest::Check_Position_Inheritance_Propagation", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + std::vector Nodes = PopulateSceneGraphTinySize(CurrentScene); + FEEntity* Entity_0 = reinterpret_cast(Nodes[0]->GetEntity()); + FEEntity* Entity_1 = reinterpret_cast(Nodes[1]->GetEntity()); + + // Update only the parent node's transformation. + Entity_0->GetComponent().SetPosition(glm::vec3(14.0f, 18.0f, -5.0f)); + + // Force update + ENGINE.BeginFrame(); + ENGINE.Render(); + ENGINE.EndFrame(); + + // Check if the position is inherited by the child nodes. + glm::dvec3 ExpectedPosition = glm::dvec3(14.000000, 18.000000, -5.000000); + glm::mat4 ChildTransformMatrix = Entity_1->GetComponent().GetWorldMatrix(); + glm::dvec3 ActualPosition; + glm::dquat ActualRotation; + glm::dvec3 ActualScale; + GEOMETRY.DecomposeMatrixToTranslationRotationScale(ChildTransformMatrix, ActualPosition, ActualRotation, ActualScale); + // Code to generate the expected values + //std::string Output = "glm::dvec3 ExpectedPosition = glm::dvec3(" + std::to_string(ActualPosition.x) + ", " + std::to_string(ActualPosition.y) + ", " + std::to_string(ActualPosition.z) + ");\n"; + //Output += "glm::dquat ExpectedRotation = glm::dquat(" + std::to_string(ActualRotation.w) + ", " + std::to_string(ActualRotation.x) + ", " + std::to_string(ActualRotation.y) + ", " + std::to_string(ActualRotation.z) + ");\n"; + //Output += "glm::dvec3 ExpectedScale = glm::dvec3(" + std::to_string(ActualScale.x) + ", " + std::to_string(ActualScale.y) + ", " + std::to_string(ActualScale.z) + ");\n"; + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualPosition, ExpectedPosition)); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Position_Inheritance_Propagation", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_Rotation_Inheritance_Propagation) +{ + LOG.Add("Starting SceneGraphTest::Check_Rotation_Inheritance_Propagation", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + std::vector Nodes = PopulateSceneGraphTinySize(CurrentScene); + FEEntity* Entity_0 = reinterpret_cast(Nodes[0]->GetEntity()); + FEEntity* Entity_1 = reinterpret_cast(Nodes[1]->GetEntity()); + + // Update parent node's rotation + glm::quat Rotation = glm::angleAxis(glm::radians(45.0f), glm::vec3(0, 1, 0)); + Entity_0->GetComponent().SetQuaternion(Rotation); + + // Force update + ENGINE.BeginFrame(); + ENGINE.Render(); + ENGINE.EndFrame(); + + // Check if rotation is inherited by child nodes + glm::dquat ExpectedRotation = glm::dquat(0.923880, 0.000000, 0.382683, 0.000000); + glm::mat4 ChildTransformMatrix = Entity_1->GetComponent().GetWorldMatrix(); + glm::dvec3 ActualPosition; + glm::dquat ActualRotation; + glm::dvec3 ActualScale; + GEOMETRY.DecomposeMatrixToTranslationRotationScale(ChildTransformMatrix, ActualPosition, ActualRotation, ActualScale); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualRotation, ExpectedRotation)); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Rotation_Inheritance_Propagation", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_Scale_Inheritance_Propagation) +{ + LOG.Add("Starting SceneGraphTest::Check_Scale_Inheritance_Propagation", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + std::vector Nodes = PopulateSceneGraphTinySize(CurrentScene); + FEEntity* Entity_0 = reinterpret_cast(Nodes[0]->GetEntity()); + FEEntity* Entity_1 = reinterpret_cast(Nodes[1]->GetEntity()); + + // Update parent node's scale + Entity_0->GetComponent().SetScale(glm::vec3(2.0f, 2.0f, 2.0f)); + + // Force update + ENGINE.BeginFrame(); + ENGINE.Render(); + ENGINE.EndFrame(); + + // Check if scale is inherited by child nodes + glm::dvec3 ExpectedScale = glm::dvec3(2.000000, 2.000000, 2.000000); + glm::mat4 ChildTransformMatrix = Entity_1->GetComponent().GetWorldMatrix(); + glm::dvec3 ActualPosition; + glm::dquat ActualRotation; + glm::dvec3 ActualScale; + GEOMETRY.DecomposeMatrixToTranslationRotationScale(ChildTransformMatrix, ActualPosition, ActualRotation, ActualScale); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualScale, ExpectedScale)); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Scale_Inheritance_Propagation", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_Multi_Level_Inheritance_Propagation_Tiny) +{ + LOG.Add("Starting SceneGraphTest::Check_Multi_Level_Inheritance_Propagation_Tiny", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + std::vector Nodes = PopulateSceneGraphTinySize(CurrentScene); + + FEEntity* Entity_0 = reinterpret_cast(Nodes[0]->GetEntity()); + FEEntity* Entity_1 = reinterpret_cast(Nodes[1]->GetEntity()); + FEEntity* Entity_2 = reinterpret_cast(Nodes[2]->GetEntity()); + FEEntity* Entity_3 = reinterpret_cast(Nodes[3]->GetEntity()); + + // Apply transformations to various nodes + Entity_0->GetComponent().SetPosition(glm::vec3(10.0f, 10.0f, 10.0f)); + Entity_1->GetComponent().SetQuaternion(glm::angleAxis(glm::radians(30.0f), glm::vec3(0, 1, 0))); + Entity_2->GetComponent().SetScale(glm::vec3(1.5f, 1.5f, 1.5f)); + + // Force update + ENGINE.BeginFrame(); + ENGINE.Render(); + ENGINE.EndFrame(); + + // Check if transformations are correctly propagated to the deepest child + glm::mat4 ExpectedMatrix = glm::mat4(1.299038, 0.000000, -0.750000, 0.000000, + 0.000000, 1.500000, 0.000000, 0.000000, + 0.750000, 0.000000, 1.299038, 0.000000, + 10.000000, 10.000000, 10.000000, 1.000000); + + glm::mat4 ActualMatrix = Entity_3->GetComponent().GetWorldMatrix(); + + // Code to generate the expected matrix + /*std::string Output = "glm::mat4 ExpectedMatrix = glm::mat4("; + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + Output += std::to_string(ActualMatrix[i][j]); + if (i < 3 || j < 3) + { + Output += ", "; + } + } + } + Output += ")";*/ + + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ExpectedMatrix, ActualMatrix)); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Multi_Level_Inheritance_Propagation_Tiny", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_Multi_Level_Inheritance_Propagation_Small) +{ + LOG.Add("Starting SceneGraphTest::Check_Multi_Level_Inheritance_Propagation_Small", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + std::vector Nodes = PopulateSceneGraphSmallSize(CurrentScene); + + FEEntity* Entity_0 = reinterpret_cast(Nodes[0]->GetEntity()); + FEEntity* Entity_1 = reinterpret_cast(Nodes[1]->GetEntity()); + FEEntity* Entity_2 = reinterpret_cast(Nodes[2]->GetEntity()); + FEEntity* Entity_6 = reinterpret_cast(Nodes[6]->GetEntity()); + FEEntity* Entity_13 = reinterpret_cast(Nodes[13]->GetEntity()); + + // Apply transformations to various nodes + Entity_0->GetComponent().SetPosition(glm::vec3(10.0f, 10.0f, 10.0f)); + Entity_1->GetComponent().SetQuaternion(glm::angleAxis(glm::radians(45.0f), glm::vec3(0, 1, 0))); + Entity_2->GetComponent().SetScale(glm::vec3(2.0f, 2.0f, 2.0f)); + Entity_6->GetComponent().SetPosition(glm::vec3(5.0f, 0.0f, 0.0f)); + + // Force update + ENGINE.BeginFrame(); + ENGINE.Render(); + ENGINE.EndFrame(); + + // Check if transformations are correctly propagated to the deepest child + glm::mat4 ExpectedMatrix = glm::mat4(2.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 2.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 2.000000, 0.000000, + 20.000000, 10.000000, 10.000000, 1.000000); + + glm::mat4 ActualMatrix = Entity_13->GetComponent().GetWorldMatrix(); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ExpectedMatrix, ActualMatrix)); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Multi_Level_Inheritance_Propagation_Small", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_Multi_Level_Inheritance_Propagation_Medium) +{ + LOG.Add("Starting SceneGraphTest::Check_Multi_Level_Inheritance_Propagation_Medium", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + std::vector Nodes = PopulateSceneGraphMediumSize(CurrentScene); + + FEEntity* Entity_0 = reinterpret_cast(Nodes[0]->GetEntity()); + FEEntity* Entity_1 = reinterpret_cast(Nodes[1]->GetEntity()); + FEEntity* Entity_2 = reinterpret_cast(Nodes[2]->GetEntity()); + FEEntity* Entity_7 = reinterpret_cast(Nodes[7]->GetEntity()); + FEEntity* Entity_16 = reinterpret_cast(Nodes[16]->GetEntity()); + FEEntity* Entity_25 = reinterpret_cast(Nodes[25]->GetEntity()); + + // Apply transformations to various nodes + Entity_0->GetComponent().SetPosition(glm::vec3(10.2f, -10.0f, 0.124f)); + Entity_1->GetComponent().SetQuaternion(glm::angleAxis(glm::radians(47.8f), glm::vec3(0, 1, 0))); + Entity_2->GetComponent().SetScale(glm::vec3(2.2f, 2.2f, 2.2f)); + Entity_7->GetComponent().SetPosition(glm::vec3(5.0f, 0.5f, -4.3f)); + Entity_16->GetComponent().SetQuaternion(glm::angleAxis(glm::radians(67.0f), glm::vec3(1, 0, 0))); + + // Force update + ENGINE.BeginFrame(); + ENGINE.Render(); + ENGINE.EndFrame(); + + // Check if transformations are correctly propagated to the deepest child + glm::mat4 ExpectedMatrix = glm::mat4(2.200000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.859609, 2.025111, 0.000000, + 0.000000, -2.025111, 0.859609, 0.000000, + 21.200001, -8.900000, -9.336001, 1.000000); + + glm::mat4 ActualMatrix = Entity_25->GetComponent().GetWorldMatrix(); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ExpectedMatrix, ActualMatrix)); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Multi_Level_Inheritance_Propagation_Medium", "FE_LOG_TEST", FE_LOG_INFO); +} + +void SceneGraphTest::SetUp() {} + +void SceneGraphTest::TearDown() +{ + //SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); +} + +std::vector SceneGraphTest::PopulateSceneGraph(FEScene* SceneToWorkWith, size_t NodeCount) +{ + std::vector Nodes; + for (size_t i = 0; i < NodeCount; i++) + { + FEEntity* Entity = SceneToWorkWith->CreateEntity("Node_" + std::to_string(i)); + Nodes.push_back(SceneToWorkWith->SceneGraph.GetNodeByEntityID(Entity->GetObjectID())); + } + + return Nodes; +} + +std::vector SceneGraphTest::PopulateSceneGraphTinySize(FEScene* SceneToWorkWith) +{ + std::vector Nodes = PopulateSceneGraph(SceneToWorkWith, 6); + + // Create a hierarchy: + // + // 0 5 + // | + // 1 + // / \ + // 4 2 + // \ + // 3 + + SceneToWorkWith->SceneGraph.MoveNode(Nodes[1]->GetObjectID(), Nodes[0]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[2]->GetObjectID(), Nodes[1]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[3]->GetObjectID(), Nodes[2]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[4]->GetObjectID(), Nodes[1]->GetObjectID()); + + return Nodes; +} + +std::vector SceneGraphTest::PopulateSceneGraphSmallSize(FEScene* SceneToWorkWith) +{ + std::vector Nodes = PopulateSceneGraph(SceneToWorkWith, 15); + + // Create a hierarchy: + // + // 0 + // / \ + // 1 2 + // /|\ \ + // 3 4 5 6 + // / | |\ + // 7 8 9 10 + // / \ / \ + // 11 12 13 14 + + SceneToWorkWith->SceneGraph.MoveNode(Nodes[1]->GetObjectID(), Nodes[0]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[2]->GetObjectID(), Nodes[0]->GetObjectID()); + + SceneToWorkWith->SceneGraph.MoveNode(Nodes[3]->GetObjectID(), Nodes[1]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[4]->GetObjectID(), Nodes[1]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[5]->GetObjectID(), Nodes[1]->GetObjectID()); + + SceneToWorkWith->SceneGraph.MoveNode(Nodes[6]->GetObjectID(), Nodes[2]->GetObjectID()); + + SceneToWorkWith->SceneGraph.MoveNode(Nodes[7]->GetObjectID(), Nodes[3]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[8]->GetObjectID(), Nodes[4]->GetObjectID()); + + SceneToWorkWith->SceneGraph.MoveNode(Nodes[9]->GetObjectID(), Nodes[6]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[10]->GetObjectID(), Nodes[6]->GetObjectID()); + + SceneToWorkWith->SceneGraph.MoveNode(Nodes[11]->GetObjectID(), Nodes[7]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[12]->GetObjectID(), Nodes[7]->GetObjectID()); + + SceneToWorkWith->SceneGraph.MoveNode(Nodes[13]->GetObjectID(), Nodes[9]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[14]->GetObjectID(), Nodes[9]->GetObjectID()); + + return Nodes; +} + +std::vector SceneGraphTest::PopulateSceneGraphMediumSize(FEScene* SceneToWorkWith) +{ + std::vector Nodes = PopulateSceneGraph(SceneToWorkWith, 30); + + // Create a hierarchy: + // + // 0 + // / | \ + // 1 2 3 + // / | \ / | \ / | \ + // 4 5 6 7 8 9 10 11 12 + // /\ | | | | | | |\ + // 13 14 15 16 17 18 19 20 21 22 + // | | | | | | \ + // 23 24 25 26 27 28 29 + + // Level 1 + SceneToWorkWith->SceneGraph.MoveNode(Nodes[1]->GetObjectID(), Nodes[0]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[2]->GetObjectID(), Nodes[0]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[3]->GetObjectID(), Nodes[0]->GetObjectID()); + + // Level 2 + for (int i = 1; i <= 3; i++) + { + for (int j = 0; j < 3; j++) + { + SceneToWorkWith->SceneGraph.MoveNode(Nodes[3 * i + j + 1]->GetObjectID(), Nodes[i]->GetObjectID()); + } + } + + // Level 3 + SceneToWorkWith->SceneGraph.MoveNode(Nodes[13]->GetObjectID(), Nodes[4]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[14]->GetObjectID(), Nodes[4]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[15]->GetObjectID(), Nodes[5]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[16]->GetObjectID(), Nodes[7]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[17]->GetObjectID(), Nodes[8]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[18]->GetObjectID(), Nodes[9]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[19]->GetObjectID(), Nodes[10]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[20]->GetObjectID(), Nodes[11]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[21]->GetObjectID(), Nodes[12]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[22]->GetObjectID(), Nodes[12]->GetObjectID()); + + // Level 4 + SceneToWorkWith->SceneGraph.MoveNode(Nodes[23]->GetObjectID(), Nodes[13]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[24]->GetObjectID(), Nodes[15]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[25]->GetObjectID(), Nodes[16]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[26]->GetObjectID(), Nodes[18]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[27]->GetObjectID(), Nodes[20]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[28]->GetObjectID(), Nodes[21]->GetObjectID()); + SceneToWorkWith->SceneGraph.MoveNode(Nodes[29]->GetObjectID(), Nodes[22]->GetObjectID()); + + return Nodes; +} + +void SceneGraphTest::TestTransformationComponentAfterChildAdded(FEScene* SceneToWorkWith, const std::string& ComponentType, const glm::vec3& InitialParentTransform, const glm::vec3& InitialChildTransform) +{ + FEEntity* Entity_A = SceneToWorkWith->CreateEntity("A"); + FEEntity* Entity_B = SceneToWorkWith->CreateEntity("B"); + + // Set transformations based on type + if (ComponentType == "POSITION") + { + Entity_A->GetComponent().SetPosition(InitialParentTransform); + Entity_B->GetComponent().SetPosition(InitialChildTransform); + } + else if (ComponentType == "ROTATION") + { + Entity_A->GetComponent().SetQuaternion(glm::quat(InitialParentTransform)); + Entity_B->GetComponent().SetQuaternion(glm::quat(InitialChildTransform)); + } + else if (ComponentType == "SCALE") + { + Entity_A->GetComponent().SetScale(InitialParentTransform); + Entity_B->GetComponent().SetScale(InitialChildTransform); + } + + // Temporary using old style entities. + std::string Node_A_ID = SceneToWorkWith->SceneGraph.GetNodeByEntityID(Entity_A->GetObjectID())->GetObjectID(); + std::string Node_B_ID = SceneToWorkWith->SceneGraph.GetNodeByEntityID(Entity_B->GetObjectID())->GetObjectID(); + + SceneToWorkWith->SceneGraph.MoveNode(Node_B_ID, Node_A_ID); + + ASSERT_TRUE(ValidateTransformConsistency(Entity_B->GetComponent())); + + // Check transformation + if (ComponentType == "POSITION") + { + glm::vec3 ExpectedPosition = InitialChildTransform - InitialParentTransform; + glm::vec3 ActualPosition = Entity_B->GetComponent().GetPosition(); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualPosition, ExpectedPosition)); + } + else if (ComponentType == "ROTATION") + { + glm::quat ExpectedRotation = -(glm::inverse(glm::quat(InitialParentTransform)) * glm::quat(InitialChildTransform)); + glm::quat ActualRotation = Entity_B->GetComponent().GetQuaternion(); + // Temporary code to check with UE5 values. + glm::vec3 ExpectedEuler = glm::degrees(glm::eulerAngles(ExpectedRotation)); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualRotation, ExpectedRotation)); + } + else if (ComponentType == "SCALE") + { + glm::vec3 ExpectedScale = InitialChildTransform / InitialParentTransform; + glm::vec3 ActualScale = Entity_B->GetComponent().GetScale(); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualScale, ExpectedScale)); + } +} + +bool SceneGraphTest::ValidateTransformConsistency(FETransformComponent& Transform) +{ + glm::dvec3 WorldPosition = Transform.GetPosition(FE_WORLD_SPACE); + glm::dquat WorldRotation = Transform.GetQuaternion(FE_WORLD_SPACE); + glm::dvec3 WorldScale = Transform.GetScale(FE_WORLD_SPACE); + + glm::mat4 TransformMatrix = Transform.GetWorldMatrix(); + glm::dvec3 PositionFromMatrix; + glm::dquat RotationFromMatrix; + glm::dvec3 ScaleFromMatrix; + GEOMETRY.DecomposeMatrixToTranslationRotationScale(TransformMatrix, PositionFromMatrix, RotationFromMatrix, ScaleFromMatrix); + + // Check if GetPosition() returns the same value as the position from the matrix + if (!GEOMETRY.IsEpsilonEqual(WorldPosition, PositionFromMatrix)) + return false; + + // Check if GetQuaternion() returns the same value as the rotation from the matrix + if (!GEOMETRY.IsEpsilonEqual(WorldRotation, RotationFromMatrix)) + return false; + + // Check if GetScale() returns the same value as the scale from the matrix + if (!GEOMETRY.IsEpsilonEqual(WorldScale, ScaleFromMatrix)) + return false; + + glm::dvec3 LocalPosition = Transform.GetPosition(); + glm::dquat LocalRotation = Transform.GetQuaternion(); + glm::dvec3 LocalScale = Transform.GetScale(); + + TransformMatrix = Transform.GetLocalMatrix(); + GEOMETRY.DecomposeMatrixToTranslationRotationScale(TransformMatrix, PositionFromMatrix, RotationFromMatrix, ScaleFromMatrix); + + // Check if GetPosition() returns the same value as the position from the matrix + if (!GEOMETRY.IsEpsilonEqual(LocalPosition, PositionFromMatrix)) + return false; + + // Check if GetQuaternion() returns the same value as the rotation from the matrix + if (!GEOMETRY.IsEpsilonEqual(LocalRotation, RotationFromMatrix)) + return false; + + // Check if GetScale() returns the same value as the scale from the matrix + if (!GEOMETRY.IsEpsilonEqual(LocalScale, ScaleFromMatrix)) + return false; + + return true; +} + +void SceneGraphTest::TestTransformationAfterChildAdded(FEScene* SceneToWorkWith, FETransformComponent& InitialParentTransform, FETransformComponent& InitialChildTransform) +{ + FEEntity* Entity_A = SceneToWorkWith->CreateEntity("A"); + FEEntity* Entity_B = SceneToWorkWith->CreateEntity("B"); + + Entity_A->GetComponent().SetPosition(InitialParentTransform.GetPosition()); + Entity_B->GetComponent().SetPosition(InitialChildTransform.GetPosition()); + + Entity_A->GetComponent().SetQuaternion(InitialParentTransform.GetQuaternion()); + Entity_B->GetComponent().SetQuaternion(InitialChildTransform.GetQuaternion()); + + Entity_A->GetComponent().SetScale(InitialParentTransform.GetScale()); + Entity_B->GetComponent().SetScale(InitialChildTransform.GetScale()); + + // Temporary using old style entities. + std::string Node_A_ID = SceneToWorkWith->SceneGraph.GetNodeByEntityID(Entity_A->GetObjectID())->GetObjectID(); + std::string Node_B_ID = SceneToWorkWith->SceneGraph.GetNodeByEntityID(Entity_B->GetObjectID())->GetObjectID(); + + SceneToWorkWith->SceneGraph.MoveNode(Node_B_ID, Node_A_ID); + + ASSERT_TRUE(ValidateTransformConsistency(Entity_B->GetComponent())); + + // Get the local matrices + glm::mat4 ParentLocalMatrix = InitialParentTransform.GetLocalMatrix(); + glm::mat4 ChildLocalMatrix = InitialChildTransform.GetLocalMatrix(); + + // Calculate the inverse of the parent's local matrix + glm::mat4 ParentLocalInverseMatrix = glm::inverse(ParentLocalMatrix); + + // Calculate the new final matrix for the child + glm::mat4 ChildFinalMatrix = ParentLocalInverseMatrix * ChildLocalMatrix; + + glm::dvec3 ExpectedScale; + glm::dquat ExpectedRotation; + glm::dvec3 ExpectedPosition; + if (GEOMETRY.DecomposeMatrixToTranslationRotationScale(ChildFinalMatrix, ExpectedPosition, ExpectedRotation, ExpectedScale)) + { + glm::dvec3 ActualPosition = Entity_B->GetComponent().GetPosition(); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualPosition, ExpectedPosition)); + + glm::dquat ActualRotation = Entity_B->GetComponent().GetQuaternion(); + // Temporary code to check with UE5 values. + glm::vec3 ExpectedEuler = glm::degrees(glm::eulerAngles(ExpectedRotation)); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualRotation, ExpectedRotation)); + + glm::dvec3 ActualScale = Entity_B->GetComponent().GetScale(); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualScale, ExpectedScale)); + } +} + +void SceneGraphTest::TestTransformationComponentAfterChildChangedParent(FEScene* SceneToWorkWith, const std::string& ComponentType, const glm::vec3& InitialParentTransform, const glm::vec3& InitialChildTransform) +{ + FEEntity* Entity_A = SceneToWorkWith->CreateEntity("A"); + FEEntity* Entity_B = SceneToWorkWith->CreateEntity("B"); + + // Set transformations based on type + if (ComponentType == "POSITION") + { + Entity_A->GetComponent().SetPosition(InitialParentTransform); + Entity_B->GetComponent().SetPosition(InitialChildTransform); + } + else if (ComponentType == "ROTATION") + { + Entity_A->GetComponent().SetQuaternion(glm::quat(InitialParentTransform)); + Entity_B->GetComponent().SetQuaternion(glm::quat(InitialChildTransform)); + } + else if (ComponentType == "SCALE") + { + Entity_A->GetComponent().SetScale(InitialParentTransform); + Entity_B->GetComponent().SetScale(InitialChildTransform); + } + + // Temporary using old style entities. + std::string Node_A_ID = SceneToWorkWith->SceneGraph.GetNodeByEntityID(Entity_A->GetObjectID())->GetObjectID(); + std::string Node_B_ID = SceneToWorkWith->SceneGraph.GetNodeByEntityID(Entity_B->GetObjectID())->GetObjectID(); + + SceneToWorkWith->SceneGraph.MoveNode(Node_B_ID, Node_A_ID); + SceneToWorkWith->SceneGraph.MoveNode(Node_B_ID, SceneToWorkWith->SceneGraph.GetRoot()->GetObjectID()); + + ASSERT_TRUE(ValidateTransformConsistency(Entity_B->GetComponent())); + + // Check transformation + if (ComponentType == "POSITION") + { + glm::vec3 ExpectedPosition = InitialChildTransform; + glm::vec3 ActualPosition = Entity_B->GetComponent().GetPosition(); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualPosition, ExpectedPosition)); + } + else if (ComponentType == "ROTATION") + { + glm::quat ExpectedRotation = glm::quat(InitialChildTransform); + glm::quat ActualRotation = Entity_B->GetComponent().GetQuaternion(); + // Temporary code to check with UE5 values. + glm::vec3 ExpectedEuler = glm::degrees(glm::eulerAngles(ExpectedRotation)); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualRotation, ExpectedRotation)); + } + else if (ComponentType == "SCALE") + { + glm::vec3 ExpectedScale = InitialChildTransform; + glm::vec3 ActualScale = Entity_B->GetComponent().GetScale(); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualScale, ExpectedScale)); + } +} + +void SceneGraphTest::TestTransformationAfterChildChangedParent(FEScene* SceneToWorkWith, FETransformComponent& InitialParentTransform, FETransformComponent& InitialChildTransform) +{ + FEEntity* Entity_A = SceneToWorkWith->CreateEntity("A"); + FEEntity* Entity_B = SceneToWorkWith->CreateEntity("B"); + + Entity_A->GetComponent().SetPosition(InitialParentTransform.GetPosition()); + Entity_B->GetComponent().SetPosition(InitialChildTransform.GetPosition()); + + Entity_A->GetComponent().SetQuaternion(InitialParentTransform.GetQuaternion()); + Entity_B->GetComponent().SetQuaternion(InitialChildTransform.GetQuaternion()); + + Entity_A->GetComponent().SetScale(InitialParentTransform.GetScale()); + Entity_B->GetComponent().SetScale(InitialChildTransform.GetScale()); + + // Temporary using old style entities. + std::string Node_A_ID = SceneToWorkWith->SceneGraph.GetNodeByEntityID(Entity_A->GetObjectID())->GetObjectID(); + std::string Node_B_ID = SceneToWorkWith->SceneGraph.GetNodeByEntityID(Entity_B->GetObjectID())->GetObjectID(); + + SceneToWorkWith->SceneGraph.MoveNode(Node_B_ID, Node_A_ID); + SceneToWorkWith->SceneGraph.MoveNode(Node_B_ID, SceneToWorkWith->SceneGraph.GetRoot()->GetObjectID()); + + ASSERT_TRUE(ValidateTransformConsistency(Entity_B->GetComponent())); + + glm::vec3 ExpectedPosition = InitialChildTransform.GetPosition(); + glm::vec3 ActualPosition = Entity_B->GetComponent().GetPosition(); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualPosition, ExpectedPosition)); + + glm::quat ExpectedRotation = InitialChildTransform.GetQuaternion(); + glm::quat ActualRotation = Entity_B->GetComponent().GetQuaternion(); + // Temporary code to check with UE5 values. + glm::vec3 ExpectedEuler = glm::degrees(glm::eulerAngles(ExpectedRotation)); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualRotation, ExpectedRotation)); + + glm::vec3 ExpectedScale = InitialChildTransform.GetScale(); + glm::vec3 ActualScale = Entity_B->GetComponent().GetScale(); + ASSERT_TRUE(GEOMETRY.IsEpsilonEqual(ActualScale, ExpectedScale)); +} + +TEST_F(SceneGraphTest, Check_Basic_Transformations_After_Child_Added) +{ + LOG.Add("Starting SceneGraphTest::Check_Basic_Transformations_After_Child_Added", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + + TestTransformationComponentAfterChildAdded(CurrentScene, "POSITION", glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(-1.0f, -1.0f, -1.0f)); + TestTransformationComponentAfterChildAdded(CurrentScene, "POSITION", glm::vec3(-34.6f, 20.4f, -23.5f), glm::vec3(4.5f, -2.7f, -13.3f)); + + TestTransformationComponentAfterChildAdded(CurrentScene, "ROTATION", glm::vec3(glm::radians(5.0f), glm::radians(145.0f), glm::radians(45.0f)), glm::vec3(glm::radians(-65.0f), glm::radians(15.0f), glm::radians(90.0f))); + TestTransformationComponentAfterChildAdded(CurrentScene, "ROTATION", glm::vec3(glm::radians(-46.23f), glm::radians(175.12f), glm::radians(90.0f)), glm::vec3(glm::radians(-162.6f), glm::radians(-27.23f), glm::radians(90.0f))); + + TestTransformationComponentAfterChildAdded(CurrentScene, "SCALE", glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(0.5f, 2.0f, 1.5f)); + TestTransformationComponentAfterChildAdded(CurrentScene, "SCALE", glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(2.0f, 2.0f, 2.0f)); + + // For more complex transformations uniform scaling is used. + FETransformComponent ParentTransform; + ParentTransform.SetPosition(glm::vec3(54.6f, -167.2f, -83.9f)); + ParentTransform.SetRotation(glm::vec3(-48.9f, 155.8f, -47.8f)); + ParentTransform.SetScale(glm::vec3(1.25f, 1.25f, 1.25f)); + + FETransformComponent ChildTransform; + ChildTransform.SetPosition(glm::vec3(-34.6f, 20.4f, -23.5f)); + ChildTransform.SetRotation(glm::vec3(-76.63f, -115.76f, 176.4f)); + ChildTransform.SetScale(glm::vec3(0.67f, 0.67f, 0.67f)); + + TestTransformationAfterChildAdded(CurrentScene, ParentTransform, ChildTransform); + + TestTransformationComponentAfterChildChangedParent(CurrentScene, "POSITION", glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(-1.0f, -1.0f, -1.0f)); + TestTransformationComponentAfterChildChangedParent(CurrentScene, "POSITION", glm::vec3(-34.6f, 20.4f, -23.5f), glm::vec3(4.5f, -2.7f, -13.3f)); + + TestTransformationComponentAfterChildChangedParent(CurrentScene, "ROTATION", glm::vec3(glm::radians(5.0f), glm::radians(145.0f), glm::radians(45.0f)), glm::vec3(glm::radians(-65.0f), glm::radians(15.0f), glm::radians(90.0f))); + TestTransformationComponentAfterChildChangedParent(CurrentScene, "ROTATION", glm::vec3(glm::radians(-46.23f), glm::radians(175.12f), glm::radians(90.0f)), glm::vec3(glm::radians(-162.6f), glm::radians(-27.23f), glm::radians(90.0f))); + + TestTransformationComponentAfterChildChangedParent(CurrentScene, "SCALE", glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(0.5f, 2.0f, 1.5f)); + TestTransformationComponentAfterChildChangedParent(CurrentScene, "SCALE", glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(2.0f, 2.0f, 2.0f)); + + // For more complex transformations uniform scaling is used. + ParentTransform.SetPosition(glm::vec3(54.6f, -167.2f, -83.9f)); + ParentTransform.SetRotation(glm::vec3(-48.9f, 155.8f, -47.8f)); + ParentTransform.SetScale(glm::vec3(1.25f, 1.25f, 1.25f)); + + ChildTransform.SetPosition(glm::vec3(-34.6f, 20.4f, -23.5f)); + ChildTransform.SetRotation(glm::vec3(-76.63f, -115.76f, 176.4f)); + ChildTransform.SetScale(glm::vec3(0.67f, 0.67f, 0.67f)); + + TestTransformationAfterChildChangedParent(CurrentScene, ParentTransform, ChildTransform); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Basic_Transformations_After_Child_Added", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_Save_Load_Simple) +{ + LOG.Add("Starting SceneGraphTest::Check_Save_Load_Simple", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + std::vector Nodes = PopulateSceneGraphMediumSize(CurrentScene); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + + // Save IDs of the entities + std::vector EntityIDs; + for (FENaiveSceneGraphNode* Node : Nodes) + { + EntityIDs.push_back(reinterpret_cast(Node->GetEntity())->GetObjectID()); + } + + // Save IDs of the nodes + std::vector NodeIDs; + for (FENaiveSceneGraphNode* Node : Nodes) + { + NodeIDs.push_back(Node->GetObjectID()); + } + + // Save the scene + std::string FilePath = "SceneGraphTest_Check_Save_Load_Simple.txt"; + + Json::Value SceneHierarchy = CurrentScene->SceneGraph.ToJson(); + Json::StreamWriterBuilder Builder; + const std::string JsonFile = Json::writeString(Builder, SceneHierarchy); + + std::ofstream SceneFile; + SceneFile.open("SceneGraphTest_Check_Save_Load_Simple.txt"); + SceneFile << JsonFile; + SceneFile.close(); + + CurrentScene->Clear(); + Nodes.clear(); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + + // ****************************** Load the scene ****************************** + std::ifstream LoadSceneFile; + LoadSceneFile.open("SceneGraphTest_Check_Save_Load_Simple.txt"); + std::string FileData((std::istreambuf_iterator(LoadSceneFile)), std::istreambuf_iterator()); + LoadSceneFile.close(); + + Json::Value Root; + JSONCPP_STRING Err; + Json::CharReaderBuilder ReadBuilder; + + const std::unique_ptr Reader(ReadBuilder.newCharReader()); + ASSERT_TRUE(Reader->parse(FileData.c_str(), FileData.c_str() + FileData.size(), &Root, &Err)); + + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + CurrentScene->SceneGraph.FromJson(Root); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + + // Retrieve the nodes with IDs of original nodes. + std::vector LoadedNodes; + for (const std::string& NodeID : NodeIDs) + { + LoadedNodes.push_back(CurrentScene->SceneGraph.GetNode(NodeID)); + ASSERT_NE(LoadedNodes.back(), nullptr); + } + + // Check that scene nodes bound to correct entities. + for (size_t i = 0; i < 30; i++) + { + ASSERT_EQ(LoadedNodes[i]->GetEntity()->GetObjectID(), EntityIDs[i]); + } + + // Check if the hierarchy is correct. + // Test direct parent-child relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[1])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[2])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[3])); + + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[4])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[5])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[6])); + + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[15], LoadedNodes[24])); + + // Test grandparent relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[5])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[8])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[12])); + + // Test great-grandparent relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[2], LoadedNodes[26])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[3], LoadedNodes[29])); + + // Test long-distance relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[23])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[24])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[25])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[26])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[27])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[28])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[29])); + + // Test non-descendant relationships + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[25])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[7])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[3])); + + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[2], LoadedNodes[24])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[2], LoadedNodes[10])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[2], LoadedNodes[23])); + + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[2])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[2], LoadedNodes[1])); + + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[23], LoadedNodes[24])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[24], LoadedNodes[23])); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Save_Load_Simple", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_Save_Load_Simple_2) +{ + LOG.Add("Starting SceneGraphTest::Check_Save_Load_Simple_2", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + std::vector Nodes = PopulateSceneGraphMediumSize(CurrentScene); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + + // Save IDs of the entities + std::vector EntityIDs; + for (FENaiveSceneGraphNode* Node : Nodes) + { + EntityIDs.push_back(reinterpret_cast(Node->GetEntity())->GetObjectID()); + } + + // Save IDs of the nodes + std::vector NodeIDs; + for (FENaiveSceneGraphNode* Node : Nodes) + { + NodeIDs.push_back(Node->GetObjectID()); + } + + // Save the scene + std::string FilePath = "SceneGraphTest_Check_Save_Load_Simple.txt"; + + Json::Value SceneHierarchy = CurrentScene->SceneGraph.ToJson(); + Json::StreamWriterBuilder Builder; + const std::string JsonFile = Json::writeString(Builder, SceneHierarchy); + + std::ofstream SceneFile; + SceneFile.open("SceneGraphTest_Check_Save_Load_Simple.txt"); + SceneFile << JsonFile; + SceneFile.close(); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + Nodes.clear(); + CurrentScene = SCENE_MANAGER.CreateScene("TestScene_2"); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + // ****************************** Load the scene ****************************** + std::ifstream LoadSceneFile; + LoadSceneFile.open("SceneGraphTest_Check_Save_Load_Simple.txt"); + std::string FileData((std::istreambuf_iterator(LoadSceneFile)), std::istreambuf_iterator()); + LoadSceneFile.close(); + + Json::Value Root; + JSONCPP_STRING Err; + Json::CharReaderBuilder ReadBuilder; + + const std::unique_ptr Reader(ReadBuilder.newCharReader()); + ASSERT_TRUE(Reader->parse(FileData.c_str(), FileData.c_str() + FileData.size(), &Root, &Err)); + + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 0); + CurrentScene->SceneGraph.FromJson(Root); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + + // Retrieve the nodes with IDs of original nodes. + std::vector LoadedNodes; + for (const std::string& NodeID : NodeIDs) + { + LoadedNodes.push_back(CurrentScene->SceneGraph.GetNode(NodeID)); + ASSERT_NE(LoadedNodes.back(), nullptr); + } + + // Check that scene nodes bound to correct entities. + for (size_t i = 0; i < 30; i++) + { + ASSERT_EQ(LoadedNodes[i]->GetEntity()->GetObjectID(), EntityIDs[i]); + } + + // Check if the hierarchy is correct. + // Test direct parent-child relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[1])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[2])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[3])); + + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[4])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[5])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[6])); + + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[15], LoadedNodes[24])); + + // Test grandparent relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[5])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[8])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[12])); + + // Test great-grandparent relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[2], LoadedNodes[26])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[3], LoadedNodes[29])); + + // Test long-distance relationship + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[23])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[24])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[25])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[26])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[27])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[28])); + ASSERT_TRUE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[0], LoadedNodes[29])); + + // Test non-descendant relationships + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[25])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[7])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[3])); + + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[2], LoadedNodes[24])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[2], LoadedNodes[10])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[2], LoadedNodes[23])); + + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[1], LoadedNodes[2])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[2], LoadedNodes[1])); + + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[23], LoadedNodes[24])); + ASSERT_FALSE(CurrentScene->SceneGraph.IsDescendant(LoadedNodes[24], LoadedNodes[23])); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Save_Load_Simple_2", "FE_LOG_TEST", FE_LOG_INFO); +} + +void AddNodeAndChildsToVector(std::vector& VectorToAdd, FENaiveSceneGraphNode* Node) +{ + VectorToAdd.push_back(Node); + std::vector Childrens = Node->GetChildren(); + for (size_t i = 0; i < Node->GetImediateChildrenCount(); i++) + { + AddNodeAndChildsToVector(VectorToAdd, Childrens[i]); + } +} + +bool CheckEntityParentScene(FEScene* Scene, FENaiveSceneGraphNode* Node) +{ + if (Node == nullptr) + return false; + + if (Node != Scene->SceneGraph.GetRoot()) + { + if (Node->GetEntity() == nullptr) + return false; + + if (Node->GetEntity()->GetParentScene() != Scene) + return false; + } + + std::vector Childrens = Node->GetChildren(); + for (size_t i = 0; i < Node->GetImediateChildrenCount(); i++) + { + if (!CheckEntityParentScene(Scene, Childrens[i])) + return false; + } + + return true; +} + +TEST_F(SceneGraphTest, Check_SceneNodes_Import) +{ + LOG.Add("Starting SceneGraphTest::Check_SceneNodes_Import", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* FirstScene = SCENE_MANAGER.CreateScene("TestScene_1"); + ASSERT_EQ(FirstScene->SceneGraph.GetNodeCount(), 0); + std::vector FirstNodes = PopulateSceneGraphMediumSize(FirstScene); + for (size_t i = 0; i < FirstNodes.size(); i++) + FirstNodes[i]->SetName("S1_" + FirstNodes[i]->GetName()); + size_t FirstNodeCount = 30; + ASSERT_EQ(FirstScene->SceneGraph.GetNodeCount(), FirstNodeCount); + + FEScene* SecondScene = SCENE_MANAGER.CreateScene("TestScene_2"); + ASSERT_EQ(SecondScene->SceneGraph.GetNodeCount(), 0); + std::vector SecondNodes = PopulateSceneGraphSmallSize(SecondScene); + for (size_t i = 0; i < SecondNodes.size(); i++) + SecondNodes[i]->SetName("S2_" + SecondNodes[i]->GetName()); + size_t SecondNodeCount = 15; + ASSERT_EQ(SecondScene->SceneGraph.GetNodeCount(), SecondNodeCount); + + srand(RANDOM_SEED); + for (size_t i = 0; i < RANDOM_ACTIONS_ITERATIONS / 20; i++) + { + bool bImportFromSecondToFirst = rand() % 2; + FEScene* SourceScene = bImportFromSecondToFirst ? SecondScene : FirstScene; + FEScene* DestinationScene = bImportFromSecondToFirst ? FirstScene : SecondScene; + std::vector* SourceNodes = bImportFromSecondToFirst ? &SecondNodes : &FirstNodes; + std::vector* DestinationNodes = bImportFromSecondToFirst ? &FirstNodes : &SecondNodes; + + size_t BeforeImportNodeCountInSource = SourceScene->SceneGraph.GetNodeCount(); + size_t BeforeImportNodeCountInDestination = DestinationScene->SceneGraph.GetNodeCount(); + + size_t RandomIndexOfNodeToMove = rand() % SourceScene->SceneGraph.GetNodeCount(); + size_t RandomIndexOfNewParent = rand() % DestinationScene->SceneGraph.GetNodeCount(); + + size_t NodeToMoveChildrenCount = (*SourceNodes)[RandomIndexOfNodeToMove]->GetImediateChildrenCount(); + size_t NodeToMoveRecursiveChildCount = (*SourceNodes)[RandomIndexOfNodeToMove]->GetRecursiveChildCount(); + + size_t NewParentChildrenCount = (*DestinationNodes)[RandomIndexOfNewParent]->GetImediateChildrenCount(); + size_t NewParentRecursiveChildCount = (*DestinationNodes)[RandomIndexOfNewParent]->GetRecursiveChildCount(); + + size_t NodesAdded = NodeToMoveRecursiveChildCount + 1; + + FEEntity* ImportedEntity = DestinationScene->ImportEntity((*SourceNodes)[RandomIndexOfNodeToMove]->GetEntity(), (*DestinationNodes)[RandomIndexOfNewParent]); + FENaiveSceneGraphNode* ImportedNode = nullptr; + if (ImportedEntity) + ImportedNode = DestinationScene->SceneGraph.GetNodeByEntityID(ImportedEntity->GetObjectID()); + + if (ImportedNode) + { + ImportedNode->SetName("Imported_" + ImportedNode->GetName()); + // If node was imported, check if the counts are correct. + ASSERT_EQ((*SourceNodes)[RandomIndexOfNodeToMove]->GetImediateChildrenCount(), NodeToMoveChildrenCount); + ASSERT_EQ((*SourceNodes)[RandomIndexOfNodeToMove]->GetRecursiveChildCount(), NodeToMoveRecursiveChildCount); + ASSERT_EQ(SourceScene->SceneGraph.GetNodeCount(), BeforeImportNodeCountInSource); + + ASSERT_EQ(DestinationScene->SceneGraph.GetNodeCount(), BeforeImportNodeCountInDestination + (NodeToMoveRecursiveChildCount + 1)); + + FirstNodeCount += bImportFromSecondToFirst ? NodesAdded : 0; + SecondNodeCount += bImportFromSecondToFirst ? 0 : NodesAdded; + + // Check that all entities are bound to the correct scene. + ASSERT_TRUE(CheckEntityParentScene(FirstScene, FirstScene->SceneGraph.GetRoot())); + ASSERT_TRUE(CheckEntityParentScene(SecondScene, SecondScene->SceneGraph.GetRoot())); + + // Then adjust DestinationNodes + AddNodeAndChildsToVector((*DestinationNodes), ImportedNode); + + ASSERT_EQ((*DestinationNodes)[RandomIndexOfNewParent]->GetImediateChildrenCount(), NewParentChildrenCount + 1); + ASSERT_EQ((*DestinationNodes)[RandomIndexOfNewParent]->GetRecursiveChildCount(), NewParentRecursiveChildCount + NodeToMoveRecursiveChildCount + 1); + } + else + { + // If node was not imported, check if the counts are the same. + ASSERT_EQ((*SourceNodes)[RandomIndexOfNodeToMove]->GetImediateChildrenCount(), NodeToMoveChildrenCount); + ASSERT_EQ((*SourceNodes)[RandomIndexOfNodeToMove]->GetRecursiveChildCount(), NodeToMoveRecursiveChildCount); + + ASSERT_EQ((*DestinationNodes)[RandomIndexOfNewParent]->GetImediateChildrenCount(), NewParentChildrenCount); + ASSERT_EQ((*DestinationNodes)[RandomIndexOfNewParent]->GetRecursiveChildCount(), NewParentRecursiveChildCount); + } + + ASSERT_EQ(FirstScene->SceneGraph.GetNodeCount(), FirstNodeCount); + ASSERT_EQ(SecondScene->SceneGraph.GetNodeCount(), SecondNodeCount); + } + + ASSERT_EQ(FirstScene->SceneGraph.GetNodeCount(), FirstNodeCount); + ASSERT_EQ(SecondScene->SceneGraph.GetNodeCount(), SecondNodeCount); + SCENE_MANAGER.DeleteScene(FirstScene->GetObjectID()); + SCENE_MANAGER.DeleteScene(SecondScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_SceneNodes_Import", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Check_Delete_Nodes_and_Entities) +{ + LOG.Add("Starting SceneGraphTest::Check_Delete_Nodes_and_Entities", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + std::vector Nodes = PopulateSceneGraphMediumSize(CurrentScene); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + + // Save IDs of the entities + std::vector EntityIDs; + for (FENaiveSceneGraphNode* Node : Nodes) + { + EntityIDs.push_back(reinterpret_cast(Node->GetEntity())->GetObjectID()); + } + + // Save IDs of the nodes + std::vector NodeIDs; + for (FENaiveSceneGraphNode* Node : Nodes) + { + NodeIDs.push_back(Node->GetObjectID()); + } + + int NodeCount = 30; + // ************ Delete entity without children ************ + std::string EntityToDeleteID = Nodes[27]->GetEntity()->GetObjectID(); + std::string NodeToDeleteID = Nodes[27]->GetObjectID(); + + ASSERT_EQ(Nodes[20]->GetImediateChildrenCount(), 1); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), NodeCount); + ASSERT_EQ(Nodes[0]->GetRecursiveChildCount(), NodeCount - 1); + + CurrentScene->DeleteEntity(Nodes[27]->GetEntity()); + + // Check if the entity and the node are deleted. + ASSERT_EQ(CurrentScene->GetEntity(EntityToDeleteID), nullptr); + ASSERT_EQ(CurrentScene->SceneGraph.GetNode(NodeToDeleteID), nullptr); + + // Basic check if the hierarchy is correct. + NodeCount--; + ASSERT_EQ(Nodes[20]->GetImediateChildrenCount(), 0); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), NodeCount); + ASSERT_EQ(Nodes[0]->GetRecursiveChildCount(), NodeCount - 1); + + // ************ Delete entity with one children ************ + EntityToDeleteID = Nodes[16]->GetEntity()->GetObjectID(); + NodeToDeleteID = Nodes[16]->GetObjectID(); + + std::string ChildEntityToDeleteID = Nodes[25]->GetEntity()->GetObjectID(); + std::string ChildNodeToDeleteID = Nodes[25]->GetObjectID(); + + CurrentScene->DeleteEntity(Nodes[16]->GetEntity()); + + ASSERT_EQ(CurrentScene->GetEntity(EntityToDeleteID), nullptr); + ASSERT_EQ(CurrentScene->SceneGraph.GetNode(NodeToDeleteID), nullptr); + + ASSERT_EQ(CurrentScene->GetEntity(ChildEntityToDeleteID), nullptr); + ASSERT_EQ(CurrentScene->SceneGraph.GetNode(ChildNodeToDeleteID), nullptr); + + // Basic check if the hierarchy is correct. + NodeCount -= 2; + ASSERT_EQ(Nodes[7]->GetImediateChildrenCount(), 0); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), NodeCount); + ASSERT_EQ(Nodes[0]->GetRecursiveChildCount(), NodeCount - 1); + + // ************ Delete entity that have subtree as children ************ + EntityToDeleteID = Nodes[1]->GetEntity()->GetObjectID(); + NodeToDeleteID = Nodes[1]->GetObjectID(); + + std::vector ChildEntitiesToDelete; + std::vector ChildNodesToDelete; + + std::vector AllChildrens = Nodes[1]->GetRecursiveChildren(); + for (FENaiveSceneGraphNode* Child : AllChildrens) + { + ChildEntitiesToDelete.push_back(Child->GetEntity()->GetObjectID()); + ChildNodesToDelete.push_back(Child->GetObjectID()); + } + + CurrentScene->DeleteEntity(Nodes[1]->GetEntity()); + + ASSERT_EQ(CurrentScene->GetEntity(EntityToDeleteID), nullptr); + ASSERT_EQ(CurrentScene->SceneGraph.GetNode(NodeToDeleteID), nullptr); + + for (size_t i = 0; i < ChildEntitiesToDelete.size(); i++) + { + ASSERT_EQ(CurrentScene->GetEntity(ChildEntitiesToDelete[i]), nullptr); + ASSERT_EQ(CurrentScene->SceneGraph.GetNode(ChildNodesToDelete[i]), nullptr); + } + + // Basic check if the hierarchy is correct. + NodeCount -= 1 + static_cast(AllChildrens.size()); + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), NodeCount); + ASSERT_EQ(Nodes[0]->GetRecursiveChildCount(), NodeCount - 1); + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Check_Delete_Nodes_and_Entities", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Simple_Check_Of_Scene_Duplication) +{ + LOG.Add("Starting SceneGraphTest::Simple_Check_Of_Scene_Duplication", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* CurrentScene = SCENE_MANAGER.CreateScene("TestScene", "", FESceneFlag::Active); + PopulateSceneGraphMediumSize(CurrentScene); + // In this function we will populate Nodes vector in a little bit different way. + // It should be same way as we will use for duplicated scene. + std::vector Nodes = CurrentScene->SceneGraph.GetRoot()->GetRecursiveChildren(); + + // Save IDs of the entities + std::vector EntityIDs; + for (FENaiveSceneGraphNode* Node : Nodes) + { + EntityIDs.push_back(reinterpret_cast(Node->GetEntity())->GetObjectID()); + } + + // Save IDs of the nodes + std::vector NodeIDs; + for (FENaiveSceneGraphNode* Node : Nodes) + { + NodeIDs.push_back(Node->GetObjectID()); + } + + FEScene* DuplicatedScene = SCENE_MANAGER.DuplicateScene(CurrentScene->GetObjectID(), "TestScene_Duplicated"); + std::vector DuplicatedNodes = DuplicatedScene->SceneGraph.GetRoot()->GetRecursiveChildren(); + + // Save IDs of the entities + std::vector DuplicatedEntityIDs; + for (FENaiveSceneGraphNode* Node : DuplicatedNodes) + { + DuplicatedEntityIDs.push_back(reinterpret_cast(Node->GetEntity())->GetObjectID()); + } + + // Save IDs of the nodes + std::vector DuplicatedNodeIDs; + for (FENaiveSceneGraphNode* Node : DuplicatedNodes) + { + DuplicatedNodeIDs.push_back(Node->GetObjectID()); + } + + ASSERT_EQ(CurrentScene->SceneGraph.GetNodeCount(), 30); + ASSERT_EQ(DuplicatedScene->SceneGraph.GetNodeCount(), 30); + + ASSERT_EQ(Nodes.size(), DuplicatedNodes.size()); + ASSERT_EQ(EntityIDs.size(), DuplicatedEntityIDs.size()); + ASSERT_EQ(NodeIDs.size(), DuplicatedNodeIDs.size()); + + CheckEntityParentScene(CurrentScene, CurrentScene->SceneGraph.GetRoot()); + CheckEntityParentScene(DuplicatedScene, DuplicatedScene->SceneGraph.GetRoot()); + + // First will check if nodes and entities have different IDs. + // Also if objects are resided in different memory locations. + for (size_t i = 0; i < Nodes.size(); i++) + { + ASSERT_NE(NodeIDs[i], DuplicatedNodeIDs[i]); + ASSERT_NE(EntityIDs[i], DuplicatedEntityIDs[i]); + + ASSERT_NE(Nodes[i], DuplicatedNodes[i]); + ASSERT_NE(Nodes[i]->GetEntity(), DuplicatedNodes[i]->GetEntity()); + } + + // Check if the hierarchy is correct. + for (size_t i = 0; i < Nodes.size(); i++) + { + ASSERT_EQ(Nodes[i]->GetImediateChildrenCount(), DuplicatedNodes[i]->GetImediateChildrenCount()); + ASSERT_EQ(Nodes[i]->GetRecursiveChildCount(), DuplicatedNodes[i]->GetRecursiveChildCount()); + + for (size_t j = 0; j < Nodes.size(); j++) + { + bool bOriginalIsDescendant = CurrentScene->SceneGraph.IsDescendant(Nodes[i], Nodes[j]); + bool bDuplicatedIsDescendant = DuplicatedScene->SceneGraph.IsDescendant(DuplicatedNodes[i], DuplicatedNodes[j]); + ASSERT_EQ(bOriginalIsDescendant, bDuplicatedIsDescendant); + } + } + + SCENE_MANAGER.DeleteScene(CurrentScene->GetObjectID()); + SCENE_MANAGER.DeleteScene(DuplicatedScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Simple_Check_Of_Scene_Duplication", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Simple_Check_Of_ImportSceneAsNode) +{ + LOG.Add("Starting SceneGraphTest::Simple_Check_Of_ImportSceneAsNode", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* TargetScene = SCENE_MANAGER.CreateScene("TargetScene"); + std::vector TargetNodes = PopulateSceneGraphMediumSize(TargetScene); + ASSERT_EQ(TargetScene->SceneGraph.GetNodeCount(), 30); + + FEScene* SourceScene = SCENE_MANAGER.CreateScene("SourceScene"); + std::vector SourceNodes = PopulateSceneGraphTinySize(SourceScene); + ASSERT_EQ(SourceScene->SceneGraph.GetNodeCount(), 6); + + ASSERT_TRUE(!SCENE_MANAGER.ImportSceneAsNode(SourceScene, TargetScene, TargetNodes[11]).empty()); + + // Check if source scene is still valid. + ASSERT_EQ(SourceScene->SceneGraph.GetNodeCount(), 6); + ASSERT_TRUE(SourceScene->SceneGraph.IsDescendant(SourceNodes[0], SourceNodes[1])); + ASSERT_FALSE(SourceScene->SceneGraph.IsDescendant(SourceNodes[1], SourceNodes[0])); + ASSERT_FALSE(SourceScene->SceneGraph.IsDescendant(SourceNodes[0], SourceNodes[5])); + ASSERT_FALSE(SourceScene->SceneGraph.IsDescendant(SourceNodes[5], SourceNodes[0])); + ASSERT_TRUE(SourceScene->SceneGraph.IsDescendant(SourceNodes[1], SourceNodes[2])); + ASSERT_FALSE(SourceScene->SceneGraph.IsDescendant(SourceNodes[2], SourceNodes[1])); + ASSERT_TRUE(SourceScene->SceneGraph.IsDescendant(SourceNodes[1], SourceNodes[4])); + ASSERT_FALSE(SourceScene->SceneGraph.IsDescendant(SourceNodes[4], SourceNodes[1])); + ASSERT_TRUE(SourceScene->SceneGraph.IsDescendant(SourceNodes[2], SourceNodes[3])); + ASSERT_FALSE(SourceScene->SceneGraph.IsDescendant(SourceNodes[3], SourceNodes[2])); + + // Check if target scene is what we expected. + ASSERT_EQ(TargetScene->SceneGraph.GetNodeCount(), 36); + ASSERT_EQ(TargetNodes[11]->GetImediateChildrenCount(), 3); + ASSERT_EQ(TargetNodes[11]->GetRecursiveChildCount(), 8); + + CheckEntityParentScene(SourceScene, SourceScene->SceneGraph.GetRoot()); + CheckEntityParentScene(TargetScene, TargetScene->SceneGraph.GetRoot()); + + std::vector Childrens = TargetNodes[11]->GetRecursiveChildren(); + int FoundChildrenCount = 0; + for (size_t i = 0; i < Childrens.size(); i++) + { + if (Childrens[i]->GetName() == "Node_0") + FoundChildrenCount++; + if (Childrens[i]->GetName() == "Node_1") + FoundChildrenCount++; + if (Childrens[i]->GetName() == "Node_2") + FoundChildrenCount++; + if (Childrens[i]->GetName() == "Node_3") + FoundChildrenCount++; + if (Childrens[i]->GetName() == "Node_4") + FoundChildrenCount++; + if (Childrens[i]->GetName() == "Node_5") + FoundChildrenCount++; + + if (Childrens[i]->GetName() == "Node_20") + FoundChildrenCount++; + if (Childrens[i]->GetName() == "Node_27") + FoundChildrenCount++; + } + ASSERT_EQ(FoundChildrenCount, 8); + + SCENE_MANAGER.DeleteScene(TargetScene->GetObjectID()); + SCENE_MANAGER.DeleteScene(SourceScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Simple_Check_Of_ImportSceneAsNode", "FE_LOG_TEST", FE_LOG_INFO); +} + +TEST_F(SceneGraphTest, Simple_Check_Of_AreSceneGraphHierarchiesEquivalent) +{ + LOG.Add("Starting SceneGraphTest::Simple_Check_Of_AreSceneGraphHierarchiesEquivalent", "FE_LOG_TEST", FE_LOG_INFO); + + FEScene* FirstMediumSizeScene = SCENE_MANAGER.CreateScene("FirstMediumSizeScene"); + std::vector FirstMediumSizeSceneNodes = PopulateSceneGraphMediumSize(FirstMediumSizeScene); + ASSERT_EQ(FirstMediumSizeScene->SceneGraph.GetNodeCount(), 30); + + FEScene* SecondMediumSizeScene = SCENE_MANAGER.CreateScene("SecondMediumSizeScene"); + std::vector SecondMediumSizeSceneNodes = PopulateSceneGraphMediumSize(SecondMediumSizeScene); + ASSERT_EQ(SecondMediumSizeScene->SceneGraph.GetNodeCount(), 30); + + ASSERT_TRUE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeScene->SceneGraph.GetRoot(), SecondMediumSizeScene->SceneGraph.GetRoot())); + + // Change the name of the first node in the second scene. + SecondMediumSizeSceneNodes[0]->SetName("Node_0_Changed"); + ASSERT_TRUE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeScene->SceneGraph.GetRoot(), SecondMediumSizeScene->SceneGraph.GetRoot())); + ASSERT_FALSE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeScene->SceneGraph.GetRoot(), SecondMediumSizeScene->SceneGraph.GetRoot(), true)); + + SecondMediumSizeSceneNodes[0]->SetName("Node_0"); + ASSERT_TRUE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeScene->SceneGraph.GetRoot(), SecondMediumSizeScene->SceneGraph.GetRoot(), true)); + SecondMediumSizeSceneNodes[10]->SetName("Node_10_Changed"); + ASSERT_FALSE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeScene->SceneGraph.GetRoot(), SecondMediumSizeScene->SceneGraph.GetRoot(), true)); + SecondMediumSizeSceneNodes[10]->SetName("Node_10"); + ASSERT_TRUE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeScene->SceneGraph.GetRoot(), SecondMediumSizeScene->SceneGraph.GetRoot(), true)); + + FirstMediumSizeScene->SceneGraph.MoveNode(FirstMediumSizeSceneNodes[20]->GetObjectID(), FirstMediumSizeSceneNodes[29]->GetObjectID()); + ASSERT_FALSE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeScene->SceneGraph.GetRoot(), SecondMediumSizeScene->SceneGraph.GetRoot())); + ASSERT_FALSE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeScene->SceneGraph.GetRoot(), SecondMediumSizeScene->SceneGraph.GetRoot(), true)); + + FirstMediumSizeScene->SceneGraph.MoveNode(FirstMediumSizeSceneNodes[20]->GetObjectID(), FirstMediumSizeSceneNodes[11]->GetObjectID()); + ASSERT_TRUE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeScene->SceneGraph.GetRoot(), SecondMediumSizeScene->SceneGraph.GetRoot())); + ASSERT_TRUE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeScene->SceneGraph.GetRoot(), SecondMediumSizeScene->SceneGraph.GetRoot(), true)); + + // Nodes are different, but they have same hierarchy. + ASSERT_TRUE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeSceneNodes[10], SecondMediumSizeSceneNodes[20])); + // But names are different. + ASSERT_FALSE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeSceneNodes[10], SecondMediumSizeSceneNodes[20], true)); + + ASSERT_TRUE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeSceneNodes[15], SecondMediumSizeSceneNodes[15])); + ASSERT_TRUE(SCENE_MANAGER.AreSceneGraphHierarchiesEquivalent(FirstMediumSizeSceneNodes[15], SecondMediumSizeSceneNodes[15], true)); + + SCENE_MANAGER.DeleteScene(FirstMediumSizeScene->GetObjectID()); + SCENE_MANAGER.DeleteScene(SecondMediumSizeScene->GetObjectID()); + + LOG.Add("Ending SceneGraphTest::Simple_Check_Of_AreSceneGraphHierarchiesEquivalent", "FE_LOG_TEST", FE_LOG_INFO); +} \ No newline at end of file diff --git a/Tests/SceneGraphTest.h b/Tests/SceneGraphTest.h new file mode 100644 index 0000000..4fda942 --- /dev/null +++ b/Tests/SceneGraphTest.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../FEngine.h" +#include "gtest/gtest.h" +using namespace FocalEngine; + +class SceneGraphTest : public ::testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + std::vector PopulateSceneGraph(FEScene* SceneToWorkWith, size_t NodeCount); + std::vector PopulateSceneGraphTinySize(FEScene* SceneToWorkWith); + std::vector PopulateSceneGraphSmallSize(FEScene* SceneToWorkWith); + std::vector PopulateSceneGraphMediumSize(FEScene* SceneToWorkWith); + + bool ValidateTransformConsistency(FETransformComponent& Transform); + + void TestTransformationComponentAfterChildAdded(FEScene* SceneToWorkWith, const std::string& ComponentType, const glm::vec3& InitialParentTransform, const glm::vec3& InitialChildTransform); + void TestTransformationAfterChildAdded(FEScene* SceneToWorkWith, FETransformComponent& InitialParentTransform, FETransformComponent& InitialChildTransform); + + void TestTransformationComponentAfterChildChangedParent(FEScene* SceneToWorkWith, const std::string& ComponentType, const glm::vec3& InitialParentTransform, const glm::vec3& InitialChildTransform); + void TestTransformationAfterChildChangedParent(FEScene* SceneToWorkWith, FETransformComponent& InitialParentTransform, FETransformComponent& InitialChildTransform); +}; \ No newline at end of file diff --git a/Tests/ThirdParty/googletest/LICENSE b/Tests/ThirdParty/googletest/LICENSE new file mode 100644 index 0000000..1941a11 --- /dev/null +++ b/Tests/ThirdParty/googletest/LICENSE @@ -0,0 +1,28 @@ +Copyright 2008, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest-assertion-result.h b/Tests/ThirdParty/googletest/include/gtest/gtest-assertion-result.h new file mode 100644 index 0000000..74eb2b1 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest-assertion-result.h @@ -0,0 +1,237 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This file implements the AssertionResult type. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_ + +#include +#include +#include +#include + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-port.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that describes how it failed. +// +// To create an instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// This class is useful for two purposes: +// 1. Defining predicate functions to be used with Boolean test assertions +// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts +// 2. Defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// For example, if you define IsEven predicate: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) +// will print the message +// +// Value of: IsEven(Fib(5)) +// Actual: false (5 is odd) +// Expected: true +// +// instead of a more opaque +// +// Value of: IsEven(Fib(5)) +// Actual: false +// Expected: true +// +// in case IsEven is a simple Boolean predicate. +// +// If you expect your predicate to be reused and want to support informative +// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up +// about half as often as positive ones in our tests), supply messages for +// both success and failure cases: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess() << n << " is even"; +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print +// +// Value of: IsEven(Fib(6)) +// Actual: true (8 is even) +// Expected: false +// +// NB: Predicates that support negative Boolean assertions have reduced +// performance in positive ones so be careful not to use them in tests +// that have lots (tens of thousands) of positive Boolean assertions. +// +// To use this class with EXPECT_PRED_FORMAT assertions such as: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() +// << "Expected: " << expr << " is even\n Actual: it's " << n; +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 + +class GTEST_API_ AssertionResult { + public: + // Copy constructor. + // Used in EXPECT_TRUE/FALSE(assertion_result). + AssertionResult(const AssertionResult& other); + +// C4800 is a level 3 warning in Visual Studio 2015 and earlier. +// This warning is not emitted in Visual Studio 2017. +// This warning is off by default starting in Visual Studio 2019 but can be +// enabled with command-line options. +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */) +#endif + + // Used in the EXPECT_TRUE/FALSE(bool_expression). + // + // T must be contextually convertible to bool. + // + // The second parameter prevents this overload from being considered if + // the argument is implicitly convertible to AssertionResult. In that case + // we want AssertionResult's copy constructor to be used. + template + explicit AssertionResult( + const T& success, + typename std::enable_if< + !std::is_convertible::value>::type* + /*enabler*/ + = nullptr) + : success_(success) {} + +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + + // Assignment operator. + AssertionResult& operator=(AssertionResult other) { + swap(other); + return *this; + } + + // Returns true if and only if the assertion succeeded. + operator bool() const { return success_; } // NOLINT + + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. + AssertionResult operator!() const; + + // Returns the text streamed into this AssertionResult. Test assertions + // use it when they fail (i.e., the predicate's outcome doesn't match the + // assertion's expectation). When nothing has been streamed into the + // object, returns an empty string. + const char* message() const { + return message_ != nullptr ? message_->c_str() : ""; + } + // Deprecated; please use message() instead. + const char* failure_message() const { return message(); } + + // Streams a custom failure message into this object. + template + AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } + + private: + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_ == nullptr) message_ = ::std::make_unique<::std::string>(); + message_->append(a_message.GetString().c_str()); + } + + // Swap the contents of this AssertionResult with other. + void swap(AssertionResult& other); + + // Stores result of the assertion predicate. + bool success_; + // Stores the message describing the condition in case the expectation + // construct is not satisfied with the predicate's outcome. + // Referenced via a pointer to avoid taking too much stack frame space + // with test assertions. + std::unique_ptr< ::std::string> message_; +}; + +// Makes a successful assertion result. +GTEST_API_ AssertionResult AssertionSuccess(); + +// Makes a failed assertion result. +GTEST_API_ AssertionResult AssertionFailure(); + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << msg. +GTEST_API_ AssertionResult AssertionFailure(const Message& msg); + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest-death-test.h b/Tests/ThirdParty/googletest/include/gtest/gtest-death-test.h new file mode 100644 index 0000000..3c61909 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest-death-test.h @@ -0,0 +1,345 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +#include "gtest/internal/gtest-death-test-internal.h" + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string_(death_test_style); + +namespace testing { + +#ifdef GTEST_HAS_DEATH_TEST + +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. It generates a warning if there is more than one active +// thread. This is because it's safe to fork() or clone() only +// when there is a single thread. +// +// 2. The parent process clone()s a sub-process and runs the death +// test in it; the sub-process exits with code 0 at the end of the +// death test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i; +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); +// +// The final parameter to each of these macros is a matcher applied to any data +// the sub-process wrote to stderr. For compatibility with existing tests, a +// bare string is interpreted as a regular expression matcher. +// +// On the regular expressions used in death tests: +// +// On POSIX-compliant systems (*nix), we use the library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows or Mac), we only support a simple regex +// syntax implemented as part of Google Test. This limited +// implementation should be enough most of the time when writing +// death tests; though it lacks many features you can find in PCRE +// or POSIX extended regex syntax. For example, we don't support +// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and +// repetition count ("x{5,7}"), among others. +// +// Below is the syntax that we do support. We chose it to be a +// subset of both PCRE and POSIX extended regex, so it's easy to +// learn wherever you come from. In the following: 'A' denotes a +// literal character, period (.), or a single \\ escape sequence; +// 'x' and 'y' denote regular expressions; 'm' and 'n' are for +// natural numbers. +// +// c matches any literal character c +// \\d matches any decimal digit +// \\D matches any character that's not a decimal digit +// \\f matches \f +// \\n matches \n +// \\r matches \r +// \\s matches any ASCII whitespace, including \n +// \\S matches any character that's not a whitespace +// \\t matches \t +// \\v matches \v +// \\w matches any letter, _, or decimal digit +// \\W matches any character that \\w doesn't match +// \\c matches any literal character c, which must be a punctuation +// . matches any single character except \n +// A? matches 0 or 1 occurrences of A +// A* matches 0 or many occurrences of A +// A+ matches 1 or many occurrences of A +// ^ matches the beginning of a string (not that of each line) +// $ matches the end of a string (not that of each line) +// xy matches x followed by y +// +// If you accidentally use PCRE or POSIX extended regex features +// not implemented by us, you will get a run-time failure. In that +// case, please try to rewrite your regular expression within the +// above syntax. +// +// This implementation is *not* meant to be as highly tuned or robust +// as a compiled regex library, but should perform well enough for a +// death test, which already incurs significant overhead by launching +// a child process. +// +// Known caveats: +// +// A "threadsafe" style death test obtains the path to the test +// program from argv[0] and re-executes it in the sub-process. For +// simplicity, the current implementation doesn't search the PATH +// when launching the sub-process. This means that the user must +// invoke the test program via a path that contains at least one +// path separator (e.g. path/to/foo_test and +// /absolute/path/to/bar_test are fine, but foo_test is not). This +// is rarely a problem as people usually don't put the test binary +// directory in PATH. +// + +// Asserts that a given `statement` causes the program to exit, with an +// integer exit status that satisfies `predicate`, and emitting error output +// that matches `matcher`. +#define ASSERT_EXIT(statement, predicate, matcher) \ + GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_FATAL_FAILURE_) + +// Like `ASSERT_EXIT`, but continues on to successive tests in the +// test suite, if any: +#define EXPECT_EXIT(statement, predicate, matcher) \ + GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_NONFATAL_FAILURE_) + +// Asserts that a given `statement` causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches `matcher`. +#define ASSERT_DEATH(statement, matcher) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher) + +// Like `ASSERT_DEATH`, but continues on to successive tests in the +// test suite, if any: +#define EXPECT_DEATH(statement, matcher) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class GTEST_API_ ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + ExitedWithCode(const ExitedWithCode&) = default; + void operator=(const ExitedWithCode& other) = delete; + bool operator()(int exit_status) const; + + private: + const int exit_code_; +}; + +#if !defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_FUCHSIA) +// Tests that an exit code describes an exit due to termination by a +// given signal. +class GTEST_API_ KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + + private: + const int signum_; +}; +#endif // !GTEST_OS_WINDOWS + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +#ifdef NDEBUG + +#define EXPECT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +#define ASSERT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +#else + +#define EXPECT_DEBUG_DEATH(statement, regex) EXPECT_DEATH(statement, regex) + +#define ASSERT_DEBUG_DEATH(statement, regex) ASSERT_DEATH(statement, regex) + +#endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST + +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// if and only if EXPECT_DEATH and ASSERT_DEATH compile with the same parameters +// on systems that support death tests. This allows one to write such a macro on +// a system that does not support death tests and be sure that it will compile +// on a death-test supporting system. It is exposed publicly so that systems +// that have death-tests with stricter requirements than GTEST_HAS_DEATH_TEST +// can write their own equivalent of EXPECT_DEATH_IF_SUPPORTED and +// ASSERT_DEATH_IF_SUPPORTED. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter if and only if EXPECT_DEATH compiles with it. +// regex_or_matcher - A regex that a macro such as EXPECT_DEATH would use +// to test the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +#define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex_or_matcher, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + +// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and +// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if +// death tests are supported; otherwise they just issue a warning. This is +// useful when you are combining death test assertions with normal test +// assertions in one test. +#ifdef GTEST_HAS_DEATH_TEST +#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH(statement, regex) +#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + ASSERT_DEATH(statement, regex) +#else +#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, ) +#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return) +#endif + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest-matchers.h b/Tests/ThirdParty/googletest/include/gtest/gtest-matchers.h new file mode 100644 index 0000000..78160f0 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest-matchers.h @@ -0,0 +1,923 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This file implements just enough of the matcher interface to allow +// EXPECT_DEATH and friends to accept a matcher argument. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-printers.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +// MSVC warning C5046 is new as of VS2017 version 15.8. +#if defined(_MSC_VER) && _MSC_VER >= 1915 +#define GTEST_MAYBE_5046_ 5046 +#else +#define GTEST_MAYBE_5046_ +#endif + +GTEST_DISABLE_MSC_WARNINGS_PUSH_( + 4251 GTEST_MAYBE_5046_ /* class A needs to have dll-interface to be used by + clients of class B */ + /* Symbol involving type with internal linkage not defined */) + +namespace testing { + +// To implement a matcher Foo for type T, define: +// 1. a class FooMatcherMatcher that implements the matcher interface: +// using is_gtest_matcher = void; +// bool MatchAndExplain(const T&, std::ostream*) const; +// (MatchResultListener* can also be used instead of std::ostream*) +// void DescribeTo(std::ostream*) const; +// void DescribeNegationTo(std::ostream*) const; +// +// 2. a factory function that creates a Matcher object from a +// FooMatcherMatcher. + +class MatchResultListener { + public: + // Creates a listener object with the given underlying ostream. The + // listener does not own the ostream, and does not dereference it + // in the constructor or destructor. + explicit MatchResultListener(::std::ostream* os) : stream_(os) {} + virtual ~MatchResultListener() = 0; // Makes this class abstract. + + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x) { + if (stream_ != nullptr) *stream_ << x; + return *this; + } + + // Returns the underlying ostream. + ::std::ostream* stream() { return stream_; } + + // Returns true if and only if the listener is interested in an explanation + // of the match result. A matcher's MatchAndExplain() method can use + // this information to avoid generating the explanation when no one + // intends to hear it. + bool IsInterested() const { return stream_ != nullptr; } + + private: + ::std::ostream* const stream_; + + MatchResultListener(const MatchResultListener&) = delete; + MatchResultListener& operator=(const MatchResultListener&) = delete; +}; + +inline MatchResultListener::~MatchResultListener() = default; + +// An instance of a subclass of this knows how to describe itself as a +// matcher. +class GTEST_API_ MatcherDescriberInterface { + public: + virtual ~MatcherDescriberInterface() = default; + + // Describes this matcher to an ostream. The function should print + // a verb phrase that describes the property a value matching this + // matcher should have. The subject of the verb phrase is the value + // being matched. For example, the DescribeTo() method of the Gt(7) + // matcher prints "is greater than 7". + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. For + // example, if the description of this matcher is "is greater than + // 7", the negated description could be "is not greater than 7". + // You are not required to override this when implementing + // MatcherInterface, but it is highly advised so that your matcher + // can produce good error messages. + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "not ("; + DescribeTo(os); + *os << ")"; + } +}; + +// The implementation of a matcher. +template +class MatcherInterface : public MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener' if necessary (see the next paragraph), in + // the form of a non-restrictive relative clause ("which ...", + // "whose ...", etc) that describes x. For example, the + // MatchAndExplain() method of the Pointee(...) matcher should + // generate an explanation like "which points to ...". + // + // Implementations of MatchAndExplain() should add an explanation of + // the match result *if and only if* they can provide additional + // information that's not already present (or not obvious) in the + // print-out of x and the matcher's description. Whether the match + // succeeds is not a factor in deciding whether an explanation is + // needed, as sometimes the caller needs to print a failure message + // when the match succeeds (e.g. when the matcher is used inside + // Not()). + // + // For example, a "has at least 10 elements" matcher should explain + // what the actual element count is, regardless of the match result, + // as it is useful information to the reader; on the other hand, an + // "is empty" matcher probably only needs to explain what the actual + // size is when the match fails, as it's redundant to say that the + // size is 0 when the value is already known to be empty. + // + // You should override this method when defining a new matcher. + // + // It's the responsibility of the caller (Google Test) to guarantee + // that 'listener' is not NULL. This helps to simplify a matcher's + // implementation when it doesn't care about the performance, as it + // can talk to 'listener' without checking its validity first. + // However, in order to implement dummy listeners efficiently, + // listener->stream() may be NULL. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Inherits these methods from MatcherDescriberInterface: + // virtual void DescribeTo(::std::ostream* os) const = 0; + // virtual void DescribeNegationTo(::std::ostream* os) const; +}; + +namespace internal { + +// A match result listener that ignores the explanation. +class DummyMatchResultListener : public MatchResultListener { + public: + DummyMatchResultListener() : MatchResultListener(nullptr) {} + + private: + DummyMatchResultListener(const DummyMatchResultListener&) = delete; + DummyMatchResultListener& operator=(const DummyMatchResultListener&) = delete; +}; + +// A match result listener that forwards the explanation to a given +// ostream. The difference between this and MatchResultListener is +// that the former is concrete. +class StreamMatchResultListener : public MatchResultListener { + public: + explicit StreamMatchResultListener(::std::ostream* os) + : MatchResultListener(os) {} + + private: + StreamMatchResultListener(const StreamMatchResultListener&) = delete; + StreamMatchResultListener& operator=(const StreamMatchResultListener&) = + delete; +}; + +struct SharedPayloadBase { + std::atomic ref{1}; + void Ref() { ref.fetch_add(1, std::memory_order_relaxed); } + bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; } +}; + +template +struct SharedPayload : SharedPayloadBase { + explicit SharedPayload(const T& v) : value(v) {} + explicit SharedPayload(T&& v) : value(std::move(v)) {} + + static void Destroy(SharedPayloadBase* shared) { + delete static_cast(shared); + } + + T value; +}; + +// An internal class for implementing Matcher, which will derive +// from it. We put functionalities common to all Matcher +// specializations here to avoid code duplication. +template +class MatcherBase : private MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener'. + bool MatchAndExplain(const T& x, MatchResultListener* listener) const { + GTEST_CHECK_(vtable_ != nullptr); + return vtable_->match_and_explain(*this, x, listener); + } + + // Returns true if and only if this matcher matches x. + bool Matches(const T& x) const { + DummyMatchResultListener dummy; + return MatchAndExplain(x, &dummy); + } + + // Describes this matcher to an ostream. + void DescribeTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, false); + } + + // Describes the negation of this matcher to an ostream. + void DescribeNegationTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, true); + } + + // Explains why x matches, or doesn't match, the matcher. + void ExplainMatchResultTo(const T& x, ::std::ostream* os) const { + StreamMatchResultListener listener(os); + MatchAndExplain(x, &listener); + } + + // Returns the describer for this matcher object; retains ownership + // of the describer, which is only guaranteed to be alive when + // this matcher object is alive. + const MatcherDescriberInterface* GetDescriber() const { + if (vtable_ == nullptr) return nullptr; + return vtable_->get_describer(*this); + } + + protected: + MatcherBase() : vtable_(nullptr), buffer_() {} + + // Constructs a matcher from its implementation. + template + explicit MatcherBase(const MatcherInterface* impl) + : vtable_(nullptr), buffer_() { + Init(impl); + } + + template ::type::is_gtest_matcher> + MatcherBase(M&& m) : vtable_(nullptr), buffer_() { // NOLINT + Init(std::forward(m)); + } + + MatcherBase(const MatcherBase& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + if (IsShared()) buffer_.shared->Ref(); + } + + MatcherBase& operator=(const MatcherBase& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + if (IsShared()) buffer_.shared->Ref(); + return *this; + } + + MatcherBase(MatcherBase&& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + other.vtable_ = nullptr; + } + + MatcherBase& operator=(MatcherBase&& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + other.vtable_ = nullptr; + return *this; + } + + ~MatcherBase() override { Destroy(); } + + private: + struct VTable { + bool (*match_and_explain)(const MatcherBase&, const T&, + MatchResultListener*); + void (*describe)(const MatcherBase&, std::ostream*, bool negation); + // Returns the captured object if it implements the interface, otherwise + // returns the MatcherBase itself. + const MatcherDescriberInterface* (*get_describer)(const MatcherBase&); + // Called on shared instances when the reference count reaches 0. + void (*shared_destroy)(SharedPayloadBase*); + }; + + bool IsShared() const { + return vtable_ != nullptr && vtable_->shared_destroy != nullptr; + } + + // If the implementation uses a listener, call that. + template + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) { + return P::Get(m).MatchAndExplain(value, listener->stream()); + } + + template + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener)) { + return P::Get(m).MatchAndExplain(value, listener); + } + + template + static void DescribeImpl(const MatcherBase& m, std::ostream* os, + bool negation) { + if (negation) { + P::Get(m).DescribeNegationTo(os); + } else { + P::Get(m).DescribeTo(os); + } + } + + template + static const MatcherDescriberInterface* GetDescriberImpl( + const MatcherBase& m) { + // If the impl is a MatcherDescriberInterface, then return it. + // Otherwise use MatcherBase itself. + // This allows us to implement the GetDescriber() function without support + // from the impl, but some users really want to get their impl back when + // they call GetDescriber(). + // We use std::get on a tuple as a workaround of not having `if constexpr`. + return std::get<( + std::is_convertible::value + ? 1 + : 0)>(std::make_tuple(&m, &P::Get(m))); + } + + template + const VTable* GetVTable() { + static constexpr VTable kVTable = {&MatchAndExplainImpl

, + &DescribeImpl

, &GetDescriberImpl

, + P::shared_destroy}; + return &kVTable; + } + + union Buffer { + // Add some types to give Buffer some common alignment/size use cases. + void* ptr; + double d; + int64_t i; + // And add one for the out-of-line cases. + SharedPayloadBase* shared; + }; + + void Destroy() { + if (IsShared() && buffer_.shared->Unref()) { + vtable_->shared_destroy(buffer_.shared); + } + } + + template + static constexpr bool IsInlined() { + return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) && + std::is_trivially_copy_constructible::value && + std::is_trivially_destructible::value; + } + + template ()> + struct ValuePolicy { + static const M& Get(const MatcherBase& m) { + // When inlined along with Init, need to be explicit to avoid violating + // strict aliasing rules. + const M* ptr = + static_cast(static_cast(&m.buffer_)); + return *ptr; + } + static void Init(MatcherBase& m, M impl) { + ::new (static_cast(&m.buffer_)) M(impl); + } + static constexpr auto shared_destroy = nullptr; + }; + + template + struct ValuePolicy { + using Shared = SharedPayload; + static const M& Get(const MatcherBase& m) { + return static_cast(m.buffer_.shared)->value; + } + template + static void Init(MatcherBase& m, Arg&& arg) { + m.buffer_.shared = new Shared(std::forward(arg)); + } + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template + struct ValuePolicy*, B> { + using M = const MatcherInterface; + using Shared = SharedPayload>; + static const M& Get(const MatcherBase& m) { + return *static_cast(m.buffer_.shared)->value; + } + static void Init(MatcherBase& m, M* impl) { + m.buffer_.shared = new Shared(std::unique_ptr(impl)); + } + + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template + void Init(M&& m) { + using MM = typename std::decay::type; + using Policy = ValuePolicy; + vtable_ = GetVTable(); + Policy::Init(*this, std::forward(m)); + } + + const VTable* vtable_; + Buffer buffer_; +}; + +} // namespace internal + +// A Matcher is a copyable and IMMUTABLE (except by assignment) +// object that can check whether a value of type T matches. The +// implementation of Matcher is just a std::shared_ptr to const +// MatcherInterface. Don't inherit from Matcher! +template +class Matcher : public internal::MatcherBase { + public: + // Constructs a null matcher. Needed for storing Matcher objects in STL + // containers. A default-constructed matcher is not yet initialized. You + // cannot use it until a valid value has been assigned to it. + explicit Matcher() {} // NOLINT + + // Constructs a matcher from its implementation. + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template + explicit Matcher( + const MatcherInterface* impl, + typename std::enable_if::value>::type* = + nullptr) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) : internal::MatcherBase(std::forward(m)) {} // NOLINT + + // Implicit constructor here allows people to write + // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes + Matcher(T value); // NOLINT +}; + +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a std::string +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() = default; + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() = default; + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() = default; + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) { + } + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() = default; + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +// Prints a matcher in a human-readable format. +template +std::ostream& operator<<(std::ostream& os, const Matcher& matcher) { + matcher.DescribeTo(&os); + return os; +} + +// The PolymorphicMatcher class template makes it easy to implement a +// polymorphic matcher (i.e. a matcher that can match values of more +// than one type, e.g. Eq(n) and NotNull()). +// +// To define a polymorphic matcher, a user should provide an Impl +// class that has a DescribeTo() method and a DescribeNegationTo() +// method, and define a member function (or member function template) +// +// bool MatchAndExplain(const Value& value, +// MatchResultListener* listener) const; +// +// See the definition of NotNull() for a complete example. +template +class PolymorphicMatcher { + public: + explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {} + + // Returns a mutable reference to the underlying matcher + // implementation object. + Impl& mutable_impl() { return impl_; } + + // Returns an immutable reference to the underlying matcher + // implementation object. + const Impl& impl() const { return impl_; } + + template + operator Matcher() const { + return Matcher(new MonomorphicImpl(impl_)); + } + + private: + template + class MonomorphicImpl : public MatcherInterface { + public: + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + void DescribeTo(::std::ostream* os) const override { impl_.DescribeTo(os); } + + void DescribeNegationTo(::std::ostream* os) const override { + impl_.DescribeNegationTo(os); + } + + bool MatchAndExplain(T x, MatchResultListener* listener) const override { + return impl_.MatchAndExplain(x, listener); + } + + private: + const Impl impl_; + }; + + Impl impl_; +}; + +// Creates a matcher from its implementation. +// DEPRECATED: Especially in the generic code, prefer: +// Matcher(new MyMatcherImpl(...)); +// +// MakeMatcher may create a Matcher that accepts its argument by value, which +// leads to unnecessary copies & lack of support for non-copyable types. +template +inline Matcher MakeMatcher(const MatcherInterface* impl) { + return Matcher(impl); +} + +// Creates a polymorphic matcher from its implementation. This is +// easier to use than the PolymorphicMatcher constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicMatcher(foo); +// vs +// PolymorphicMatcher(foo); +template +inline PolymorphicMatcher MakePolymorphicMatcher(const Impl& impl) { + return PolymorphicMatcher(impl); +} + +namespace internal { +// Implements a matcher that compares a given value with a +// pre-supplied value using one of the ==, <=, <, etc, operators. The +// two values being compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq(5) can be +// used to match an int, a short, a double, etc). Therefore we use +// a template type conversion operator in the implementation. +// +// The following template definition assumes that the Rhs parameter is +// a "bare" type (i.e. neither 'const T' nor 'T&'). +template +class ComparisonBase { + public: + explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {} + + using is_gtest_matcher = void; + + template + bool MatchAndExplain(const Lhs& lhs, std::ostream*) const { + return Op()(lhs, Unwrap(rhs_)); + } + void DescribeTo(std::ostream* os) const { + *os << D::Desc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + void DescribeNegationTo(std::ostream* os) const { + *os << D::NegatedDesc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + + private: + template + static const T& Unwrap(const T& v) { + return v; + } + template + static const T& Unwrap(std::reference_wrapper v) { + return v; + } + + Rhs rhs_; +}; + +template +class EqMatcher : public ComparisonBase, Rhs, std::equal_to<>> { + public: + explicit EqMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::equal_to<>>(rhs) {} + static const char* Desc() { return "is equal to"; } + static const char* NegatedDesc() { return "isn't equal to"; } +}; +template +class NeMatcher + : public ComparisonBase, Rhs, std::not_equal_to<>> { + public: + explicit NeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::not_equal_to<>>(rhs) {} + static const char* Desc() { return "isn't equal to"; } + static const char* NegatedDesc() { return "is equal to"; } +}; +template +class LtMatcher : public ComparisonBase, Rhs, std::less<>> { + public: + explicit LtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::less<>>(rhs) {} + static const char* Desc() { return "is <"; } + static const char* NegatedDesc() { return "isn't <"; } +}; +template +class GtMatcher : public ComparisonBase, Rhs, std::greater<>> { + public: + explicit GtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::greater<>>(rhs) {} + static const char* Desc() { return "is >"; } + static const char* NegatedDesc() { return "isn't >"; } +}; +template +class LeMatcher + : public ComparisonBase, Rhs, std::less_equal<>> { + public: + explicit LeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::less_equal<>>(rhs) {} + static const char* Desc() { return "is <="; } + static const char* NegatedDesc() { return "isn't <="; } +}; +template +class GeMatcher + : public ComparisonBase, Rhs, std::greater_equal<>> { + public: + explicit GeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, std::greater_equal<>>(rhs) {} + static const char* Desc() { return "is >="; } + static const char* NegatedDesc() { return "isn't >="; } +}; + +template ::value>::type> +using StringLike = T; + +// Implements polymorphic matchers MatchesRegex(regex) and +// ContainsRegex(regex), which can be used as a Matcher as long as +// T can be converted to a string. +class MatchesRegexMatcher { + public: + MatchesRegexMatcher(const RE* regex, bool full_match) + : regex_(regex), full_match_(full_match) {} + +#if GTEST_INTERNAL_HAS_STRING_VIEW + bool MatchAndExplain(const internal::StringView& s, + MatchResultListener* listener) const { + return MatchAndExplain(std::string(s), listener); + } +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != nullptr && MatchAndExplain(std::string(s), listener); + } + + // Matches anything that can convert to std::string. + // + // This is a template, not just a plain function with const std::string&, + // because absl::string_view has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const std::string s2(s); + return full_match_ ? RE::FullMatch(s2, *regex_) + : RE::PartialMatch(s2, *regex_); + } + + void DescribeTo(::std::ostream* os) const { + *os << (full_match_ ? "matches" : "contains") << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't " << (full_match_ ? "match" : "contain") + << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + private: + const std::shared_ptr regex_; + const bool full_match_; +}; +} // namespace internal + +// Matches a string that fully matches regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher MatchesRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true)); +} +template +PolymorphicMatcher MatchesRegex( + const internal::StringLike& regex) { + return MatchesRegex(new internal::RE(std::string(regex))); +} + +// Matches a string that contains regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher ContainsRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false)); +} +template +PolymorphicMatcher ContainsRegex( + const internal::StringLike& regex) { + return ContainsRegex(new internal::RE(std::string(regex))); +} + +// Creates a polymorphic matcher that matches anything equal to x. +// Note: if the parameter of Eq() were declared as const T&, Eq("foo") +// wouldn't compile. +template +inline internal::EqMatcher Eq(T x) { + return internal::EqMatcher(x); +} + +// Constructs a Matcher from a 'value' of type T. The constructed +// matcher matches any value that's equal to 'value'. +template +Matcher::Matcher(T value) { + *this = Eq(value); +} + +// Creates a monomorphic matcher that matches anything with type Lhs +// and equal to rhs. A user may need to use this instead of Eq(...) +// in order to resolve an overloading ambiguity. +// +// TypedEq(x) is just a convenient short-hand for Matcher(Eq(x)) +// or Matcher(x), but more readable than the latter. +// +// We could define similar monomorphic matchers for other comparison +// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do +// it yet as those are used much less than Eq() in practice. A user +// can always write Matcher(Lt(5)) to be explicit about the type, +// for example. +template +inline Matcher TypedEq(const Rhs& rhs) { + return Eq(rhs); +} + +// Creates a polymorphic matcher that matches anything >= x. +template +inline internal::GeMatcher Ge(Rhs x) { + return internal::GeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything > x. +template +inline internal::GtMatcher Gt(Rhs x) { + return internal::GtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything <= x. +template +inline internal::LeMatcher Le(Rhs x) { + return internal::LeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything < x. +template +inline internal::LtMatcher Lt(Rhs x) { + return internal::LtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything != x. +template +inline internal::NeMatcher Ne(Rhs x) { + return internal::NeMatcher(x); +} +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest-message.h b/Tests/ThirdParty/googletest/include/gtest/gtest-message.h new file mode 100644 index 0000000..448ac6b --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest-message.h @@ -0,0 +1,251 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include +#include +#include +#include +#include + +#include "gtest/internal/gtest-port.h" + +#ifdef GTEST_HAS_ABSL +#include + +#include "absl/strings/has_absl_stringify.h" +#include "absl/strings/str_cat.h" +#endif // GTEST_HAS_ABSL + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a stringstream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that stringstream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class GTEST_API_ Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + Message(); + + // Copy constructor. + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new ::std::stringstream) { + *ss_ << str; + } + + // Streams a non-pointer value to this object. If building a version of + // GoogleTest with ABSL, this overload is only enabled if the value does not + // have an AbslStringify definition. + template < + typename T +#ifdef GTEST_HAS_ABSL + , + typename std::enable_if::value, // NOLINT + int>::type = 0 +#endif // GTEST_HAS_ABSL + > + inline Message& operator<<(const T& val) { + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator<<; + *ss_ << val; + return *this; + } + +#ifdef GTEST_HAS_ABSL + // Streams a non-pointer value with an AbslStringify definition to this + // object. + template ::value, // NOLINT + int>::type = 0> + inline Message& operator<<(const T& val) { + // ::operator<< is needed here for a similar reason as with the non-Abseil + // version above + using ::operator<<; + *ss_ << absl::StrCat(val); + return *this; + } +#endif // GTEST_HAS_ABSL + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template + inline Message& operator<<(T* const& pointer) { // NOLINT + if (pointer == nullptr) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + return *this; + } + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator<<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator<<(bool b) { return *this << (b ? "true" : "false"); } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator<<(const wchar_t* wide_c_str); + Message& operator<<(wchar_t* wide_c_str); + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator<<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + + // Gets the text streamed to this object so far as an std::string. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + std::string GetString() const; + + private: + // We'll hold the text streamed to this object here. + const std::unique_ptr< ::std::stringstream> ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator<<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest-param-test.h b/Tests/ThirdParty/googletest/include/gtest/gtest-param-test.h new file mode 100644 index 0000000..55ee088 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest-param-test.h @@ -0,0 +1,546 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Macros and functions for implementing parameterized tests +// in Google C++ Testing and Mocking Framework (Google Test) + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_SUITE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test suite +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_SUITE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more than once) the first argument to the +// INSTANTIATE_TEST_SUITE_P macro is a prefix that will be added to the +// actual test suite name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_SUITE_P will instantiate all tests +// in the given test suite, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_SUITE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface, where T is the type of the parameter +// values. Inheriting from TestWithParam satisfies that requirement because +// TestWithParam inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-param-util.h" // IWYU pragma: export +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test suite is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test suite FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_SUITE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template +internal::ParamGenerator Range(T start, T end, IncrementT step) { + return internal::ParamGenerator( + new internal::RangeGenerator(start, end, step)); +} + +template +internal::ParamGenerator Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test suite StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_SUITE_P(StringSequence, StringTest, ValuesIn(strings)); +// +// This instantiates tests from test suite StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_SUITE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list GetParameterChars() { +// ::std::list list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list l = GetParameterChars(); +// INSTANTIATE_TEST_SUITE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename std::iterator_traits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename std::iterator_traits::value_type ParamType; + return internal::ParamGenerator( + new internal::ValuesInIteratorRangeGenerator(begin, end)); +} + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template +internal::ParamGenerator ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test suite BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_SUITE_P(NumSequence, +// BarTest, +// Values("one", "two", "three")); +// +// This instantiates tests from test suite BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_SUITE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// +template +internal::ValueArray Values(T... v) { + return internal::ValueArray(std::move(v)...); +} + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test suite FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_SUITE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { return Values(false, true); } + +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// std::tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// std::tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_SUITE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template +internal::CartesianProductHolder Combine(const Generator&... g) { + return internal::CartesianProductHolder(g...); +} + +// ConvertGenerator() wraps a parameter generator in order to cast each produced +// value through a known type before supplying it to the test suite +// +// Synopsis: +// ConvertGenerator(gen) +// - returns a generator producing the same elements as generated by gen, but +// each element is static_cast to type T before being returned +// +// It is useful when using the Combine() function to get the generated +// parameters in a custom type instead of std::tuple +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// struct ParamType { +// using TupleT = std::tuple; +// std::string animal; +// Color color; +// ParamType(TupleT t) : animal(std::get<0>(t)), color(std::get<1>(t)) {} +// }; +// class AnimalTest +// : public testing::TestWithParam {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest, +// ConvertGenerator( +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE)))); +// +template +internal::ParamConverterGenerator ConvertGenerator( + internal::ParamGenerator gen) { + return internal::ParamConverterGenerator(gen); +} + +#define TEST_P(test_suite_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public test_suite_name, \ + private ::testing::internal::GTestNonCopyable { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \ + void TestBody() override; \ + \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestPattern( \ + GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name), \ + new ::testing::internal::TestMetaFactory(), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)); \ + return 0; \ + } \ + GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static int gtest_registering_dummy_; \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +// The last argument to INSTANTIATE_TEST_SUITE_P allows the user to specify +// generator and an optional function or functor that generates custom test name +// suffixes based on the test parameters. Such a function or functor should +// accept one argument of type testing::TestParamInfo, and +// return std::string. +// +// testing::PrintToStringParamName is a builtin test suffix generator that +// returns the value of testing::PrintToString(GetParam()). +// +// Note: test names must be non-empty, unique, and may only contain ASCII +// alphanumeric characters or underscore. Because PrintToString adds quotes +// to std::string and C strings, it won't work for these types. + +#define GTEST_EXPAND_(arg) arg +#define GTEST_GET_FIRST_(first, ...) first +#define GTEST_GET_SECOND_(first, second, ...) second + +#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \ + static ::testing::internal::ParamGenerator \ + gtest_##prefix##test_suite_name##_EvalGenerator_() { \ + return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \ + } \ + static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \ + const ::testing::TestParamInfo& info) { \ + if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName, \ + DUMMY_PARAM_))); \ + auto t = std::make_tuple(__VA_ARGS__); \ + static_assert(std::tuple_size::value <= 2, \ + "Too Many Args!"); \ + } \ + return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName, \ + DUMMY_PARAM_))))(info); \ + } \ + GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static int \ + gtest_##prefix##test_suite_name##_dummy_ = \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestSuiteInstantiation( \ + GTEST_STRINGIFY_(prefix), \ + >est_##prefix##test_suite_name##_EvalGenerator_, \ + >est_##prefix##test_suite_name##_EvalGenerateName_, \ + __FILE__, __LINE__) + +// Allow Marking a Parameterized test class as not needing to be instantiated. +#define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T) \ + namespace gtest_do_not_use_outside_namespace_scope {} \ + static const ::testing::internal::MarkAsIgnored gtest_allow_ignore_##T( \ + GTEST_STRINGIFY_(T)) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TEST_CASE_P \ + static_assert(::testing::internal::InstantiateTestCase_P_IsDeprecated(), \ + ""); \ + INSTANTIATE_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest-printers.h b/Tests/ThirdParty/googletest/include/gtest/gtest-printers.h new file mode 100644 index 0000000..198a769 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest-printers.h @@ -0,0 +1,1236 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Test - The Google C++ Testing and Mocking Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// * Prefer AbslStringify(..) to operator<<(..), per https://abseil.io/tips/215. +// * Define foo::PrintTo(..) if the type already has AbslStringify(..), but an +// alternative presentation in test results is of interest. +// +// However if T is an STL-style container then it is printed element-wise +// unless foo::PrintTo(const T&, ostream*) is defined. Note that +// operator<<() is ignored for container types. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include +#include +#include // NOLINT +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTEST_HAS_ABSL +#include "absl/strings/has_absl_stringify.h" +#include "absl/strings/str_cat.h" +#endif // GTEST_HAS_ABSL +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +#if GTEST_INTERNAL_HAS_STD_SPAN +#include // NOLINT +#endif // GTEST_INTERNAL_HAS_STD_SPAN + +#if GTEST_INTERNAL_HAS_COMPARE_LIB +#include // NOLINT +#endif // GTEST_INTERNAL_HAS_COMPARE_LIB + +namespace testing { + +// Definitions in the internal* namespaces are subject to change without notice. +// DO NOT USE THEM IN USER CODE! +namespace internal { + +template +void UniversalPrint(const T& value, ::std::ostream* os); + +template +struct IsStdSpan { + static constexpr bool value = false; +}; + +#if GTEST_INTERNAL_HAS_STD_SPAN +template +struct IsStdSpan> { + static constexpr bool value = true; +}; +#endif // GTEST_INTERNAL_HAS_STD_SPAN + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +// +// NOTE: Since std::span does not have const_iterator until C++23, it would +// fail IsContainerTest before C++23. However, IsContainerTest only uses +// the presence of const_iterator to avoid treating iterators as containers +// because of iterator::iterator. Which means std::span satisfies the *intended* +// condition of IsContainerTest. +struct ContainerPrinter { + template (0)) == sizeof(IsContainer)) && + !IsRecursiveContainer::value) || + IsStdSpan::value>::type> + static void PrintValue(const T& container, std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (auto&& elem : container) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(elem, os) here as PrintTo() doesn't + // handle `elem` being a native array. + internal::UniversalPrint(elem, os); + ++count; + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; + } +}; + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +struct FunctionPointerPrinter { + template ::value>::type> + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. + *os << reinterpret_cast(p); + } + } +}; + +struct PointerPrinter { + template + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } + } +}; + +namespace internal_stream_operator_without_lexical_name_lookup { + +// The presence of an operator<< here will terminate lexical scope lookup +// straight away (even though it cannot be a match because of its argument +// types). Thus, the two operator<< calls in StreamPrinter will find only ADL +// candidates. +struct LookupBlocker {}; +void operator<<(LookupBlocker, LookupBlocker); + +struct StreamPrinter { + template ::value>::type> + // Only accept types for which we can find a streaming operator via + // ADL (possibly involving implicit conversions). + // (Use SFINAE via return type, because it seems GCC < 12 doesn't handle name + // lookup properly when we do it in the template parameter list.) + static auto PrintValue(const T& value, + ::std::ostream* os) -> decltype((void)(*os << value)) { + // Call streaming operator found by ADL, possibly with implicit conversions + // of the arguments. + *os << value; + } +}; + +} // namespace internal_stream_operator_without_lexical_name_lookup + +struct ProtobufPrinter { + // We print a protobuf using its ShortDebugString() when the string + // doesn't exceed this many characters; otherwise we print it using + // DebugString() for better readability. + static const size_t kProtobufOneLinerMaxLength = 50; + + template ::value>::type> + static void PrintValue(const T& value, ::std::ostream* os) { + std::string pretty_str = value.ShortDebugString(); + if (pretty_str.length() > kProtobufOneLinerMaxLength) { + pretty_str = "\n" + value.DebugString(); + } + *os << ("<" + pretty_str + ">"); + } +}; + +struct ConvertibleToIntegerPrinter { + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(internal::BiggestInt value, ::std::ostream* os) { + *os << value; + } +}; + +struct ConvertibleToStringViewPrinter { +#if GTEST_INTERNAL_HAS_STRING_VIEW + static void PrintValue(internal::StringView value, ::std::ostream* os) { + internal::UniversalPrint(value, os); + } +#endif +}; + +#ifdef GTEST_HAS_ABSL +struct ConvertibleToAbslStringifyPrinter { + template ::value>::type> // NOLINT + static void PrintValue(const T& value, ::std::ostream* os) { + *os << absl::StrCat(value); + } +}; +#endif // GTEST_HAS_ABSL + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, ::std::ostream* os); +struct RawBytesPrinter { + // SFINAE on `sizeof` to make sure we have a complete type. + template + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo( + static_cast( + // Load bearing cast to void* to support iOS + reinterpret_cast(std::addressof(value))), + sizeof(value), os); + } +}; + +struct FallbackPrinter { + template + static void PrintValue(const T&, ::std::ostream* os) { + *os << "(incomplete type)"; + } +}; + +// Try every printer in order and return the first one that works. +template +struct FindFirstPrinter : FindFirstPrinter {}; + +template +struct FindFirstPrinter< + T, decltype(Printer::PrintValue(std::declval(), nullptr)), + Printer, Printers...> { + using type = Printer; +}; + +// Select the best printer in the following order: +// - Print containers (they have begin/end/etc). +// - Print function pointers. +// - Print object pointers. +// - Print protocol buffers. +// - Use the stream operator, if available. +// - Print types convertible to BiggestInt. +// - Print types convertible to StringView, if available. +// - Fallback to printing the raw bytes of the object. +template +void PrintWithFallback(const T& value, ::std::ostream* os) { + using Printer = typename FindFirstPrinter< + T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter, + ProtobufPrinter, +#ifdef GTEST_HAS_ABSL + ConvertibleToAbslStringifyPrinter, +#endif // GTEST_HAS_ABSL + internal_stream_operator_without_lexical_name_lookup::StreamPrinter, + ConvertibleToIntegerPrinter, ConvertibleToStringViewPrinter, + RawBytesPrinter, FallbackPrinter>::type; + Printer::PrintValue(value, os); +} + +// FormatForComparison::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); +#ifdef __cpp_lib_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char8_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char8_t); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char32_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char32_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); +#ifdef __cpp_lib_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char8_t, ::std::u8string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char8_t, ::std::u8string); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char32_t, ::std::u32string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char32_t, ::std::u32string); + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +std::string FormatForComparisonFailureMessage(const T1& value, + const T2& /* other_operand */) { + return FormatForComparison::Format(value); +} + +// UniversalPrinter::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template +class UniversalPrinter; + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template +void PrintTo(const T& value, ::std::ostream* os) { + internal::PrintWithFallback(value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +GTEST_API_ void PrintTo(char32_t c, ::std::ostream* os); +inline void PrintTo(char16_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_(c), os); +} +#ifdef __cpp_lib_char8_t +inline void PrintTo(char8_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_(c), os); +} +#endif + +// gcc/clang __{u,}int128_t +#if defined(__SIZEOF_INT128__) +GTEST_API_ void PrintTo(__uint128_t v, ::std::ostream* os); +GTEST_API_ void PrintTo(__int128_t v, ::std::ostream* os); +#endif // __SIZEOF_INT128__ + +// The default resolution used to print floating-point values uses only +// 6 digits, which can be confusing if a test compares two values whose +// difference lies in the 7th digit. So we'd like to print out numbers +// in full precision. +// However if the value is something simple like 1.1, full will print a +// long string like 1.100000001 due to floating-point numbers not using +// a base of 10. This routiune returns an appropriate resolution for a +// given floating-point number, that is, 6 if it will be accurate, or a +// max_digits10 value (full precision) if it won't, for values between +// 0.0001 and one million. +// It does this by computing what those digits would be (by multiplying +// by an appropriate power of 10), then dividing by that power again to +// see if gets the original value back. +// A similar algorithm applies for values larger than one million; note +// that for those values, we must divide to get a six-digit number, and +// then multiply to possibly get the original value again. +template +int AppropriateResolution(FloatType val) { + int full = std::numeric_limits::max_digits10; + if (val < 0) val = -val; + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (val < 1000000) { + FloatType mulfor6 = 1e10; + // Without these static casts, the template instantiation for float would + // fail to compile when -Wdouble-promotion is enabled, as the arithmetic and + // comparison logic would promote floats to doubles. + if (val >= static_cast(100000.0)) { // 100,000 to 999,999 + mulfor6 = 1.0; + } else if (val >= static_cast(10000.0)) { + mulfor6 = 1e1; + } else if (val >= static_cast(1000.0)) { + mulfor6 = 1e2; + } else if (val >= static_cast(100.0)) { + mulfor6 = 1e3; + } else if (val >= static_cast(10.0)) { + mulfor6 = 1e4; + } else if (val >= static_cast(1.0)) { + mulfor6 = 1e5; + } else if (val >= static_cast(0.1)) { + mulfor6 = 1e6; + } else if (val >= static_cast(0.01)) { + mulfor6 = 1e7; + } else if (val >= static_cast(0.001)) { + mulfor6 = 1e8; + } else if (val >= static_cast(0.0001)) { + mulfor6 = 1e9; + } + if (static_cast(static_cast( + val * mulfor6 + (static_cast(0.5)))) / + mulfor6 == + val) + return 6; + } else if (val < static_cast(1e10)) { + FloatType divfor6 = static_cast(1.0); + if (val >= static_cast(1e9)) { // 1,000,000,000 to 9,999,999,999 + divfor6 = 10000; + } else if (val >= + static_cast(1e8)) { // 100,000,000 to 999,999,999 + divfor6 = 1000; + } else if (val >= + static_cast(1e7)) { // 10,000,000 to 99,999,999 + divfor6 = 100; + } else if (val >= static_cast(1e6)) { // 1,000,000 to 9,999,999 + divfor6 = 10; + } + if (static_cast(static_cast( + val / divfor6 + (static_cast(0.5)))) * + divfor6 == + val) + return 6; + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + return full; +} + +inline void PrintTo(float f, ::std::ostream* os) { + auto old_precision = os->precision(); + os->precision(AppropriateResolution(f)); + *os << f; + os->precision(old_precision); +} + +inline void PrintTo(double d, ::std::ostream* os) { + auto old_precision = os->precision(); + os->precision(AppropriateResolution(d)); + *os << d; + os->precision(old_precision); +} + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#ifdef __cpp_lib_char8_t +// Overloads for u8 strings. +GTEST_API_ void PrintTo(const char8_t* s, ::std::ostream* os); +inline void PrintTo(char8_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif +// Overloads for u16 strings. +GTEST_API_ void PrintTo(const char16_t* s, ::std::ostream* os); +inline void PrintTo(char16_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +// Overloads for u32 strings. +GTEST_API_ void PrintTo(const char32_t* s, ::std::ostream* os); +inline void PrintTo(char32_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::std::string. +GTEST_API_ void PrintStringTo(const ::std::string& s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::std::u8string +#ifdef __cpp_lib_char8_t +GTEST_API_ void PrintU8StringTo(const ::std::u8string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u8string& s, ::std::ostream* os) { + PrintU8StringTo(s, os); +} +#endif + +// Overloads for ::std::u16string +GTEST_API_ void PrintU16StringTo(const ::std::u16string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u16string& s, ::std::ostream* os) { + PrintU16StringTo(s, os); +} + +// Overloads for ::std::u32string +GTEST_API_ void PrintU32StringTo(const ::std::u32string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u32string& s, ::std::ostream* os) { + PrintU32StringTo(s, os); +} + +// Overloads for ::std::wstring. +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring& s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// Overload for internal::StringView. +inline void PrintTo(internal::StringView sp, ::std::ostream* os) { + PrintTo(::std::string(sp), os); +} +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; } + +#if GTEST_HAS_RTTI +inline void PrintTo(const std::type_info& info, std::ostream* os) { + *os << internal::GetTypeName(info); +} +#endif // GTEST_HAS_RTTI + +template +void PrintTo(std::reference_wrapper ref, ::std::ostream* os) { + UniversalPrinter::Print(ref.get(), os); +} + +inline const void* VoidifyPointer(const void* p) { return p; } +inline const void* VoidifyPointer(volatile const void* p) { + return const_cast(p); +} + +template +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, char) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + // We can't print the value. Just print the pointer.. + *os << "(" << (VoidifyPointer)(ptr.get()) << ")"; + } +} +template ::value && + !std::is_array::value>::type> +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, int) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + *os << "(ptr = " << (VoidifyPointer)(ptr.get()) << ", value = "; + UniversalPrinter::Print(*ptr, os); + *os << ")"; + } +} + +template +void PrintTo(const std::unique_ptr& ptr, std::ostream* os) { + (PrintSmartPointer)(ptr, os, 0); +} + +template +void PrintTo(const std::shared_ptr& ptr, std::ostream* os) { + (PrintSmartPointer)(ptr, os, 0); +} + +#if GTEST_INTERNAL_HAS_COMPARE_LIB +template +void PrintOrderingHelper(T ordering, std::ostream* os) { + if (ordering == T::less) { + *os << "(less)"; + } else if (ordering == T::greater) { + *os << "(greater)"; + } else if (ordering == T::equivalent) { + *os << "(equivalent)"; + } else { + *os << "(unknown ordering)"; + } +} + +inline void PrintTo(std::strong_ordering ordering, std::ostream* os) { + if (ordering == std::strong_ordering::equal) { + *os << "(equal)"; + } else { + PrintOrderingHelper(ordering, os); + } +} + +inline void PrintTo(std::partial_ordering ordering, std::ostream* os) { + if (ordering == std::partial_ordering::unordered) { + *os << "(unordered)"; + } else { + PrintOrderingHelper(ordering, os); + } +} + +inline void PrintTo(std::weak_ordering ordering, std::ostream* os) { + PrintOrderingHelper(ordering, os); +} +#endif + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T&, std::integral_constant, + ::std::ostream*) {} + +template +void PrintTupleTo(const T& t, std::integral_constant, + ::std::ostream* os) { + PrintTupleTo(t, std::integral_constant(), os); + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (I > 1) { + GTEST_INTENTIONAL_CONST_COND_POP_() + *os << ", "; + } + UniversalPrinter::type>::Print( + std::get(t), os); +} + +template +void PrintTo(const ::std::tuple& t, ::std::ostream* os) { + *os << "("; + PrintTupleTo(t, std::integral_constant(), os); + *os << ")"; +} + +// Overload for std::pair. +template +void PrintTo(const ::std::pair& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter::Print(value.first, os); + *os << ", "; + UniversalPrinter::Print(value.second, os); + *os << ')'; +} + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Remove any const-qualifiers before passing a type to UniversalPrinter. +template +class UniversalPrinter : public UniversalPrinter {}; + +#if GTEST_INTERNAL_HAS_ANY + +// Printer for std::any / absl::any + +template <> +class UniversalPrinter { + public: + static void Print(const Any& value, ::std::ostream* os) { + if (value.has_value()) { + *os << "value of type " << GetTypeName(value); + } else { + *os << "no value"; + } + } + + private: + static std::string GetTypeName(const Any& value) { +#if GTEST_HAS_RTTI + return internal::GetTypeName(value.type()); +#else + static_cast(value); // possibly unused + return ""; +#endif // GTEST_HAS_RTTI + } +}; + +#endif // GTEST_INTERNAL_HAS_ANY + +#if GTEST_INTERNAL_HAS_OPTIONAL + +// Printer for std::optional / absl::optional + +template +class UniversalPrinter> { + public: + static void Print(const Optional& value, ::std::ostream* os) { + *os << '('; + if (!value) { + *os << "nullopt"; + } else { + UniversalPrint(*value, os); + } + *os << ')'; + } +}; + +template <> +class UniversalPrinter { + public: + static void Print(decltype(Nullopt()), ::std::ostream* os) { + *os << "(nullopt)"; + } +}; + +#endif // GTEST_INTERNAL_HAS_OPTIONAL + +#if GTEST_INTERNAL_HAS_VARIANT + +// Printer for std::variant / absl::variant + +template +class UniversalPrinter> { + public: + static void Print(const Variant& value, ::std::ostream* os) { + *os << '('; +#ifdef GTEST_HAS_ABSL + absl::visit(Visitor{os, value.index()}, value); +#else + std::visit(Visitor{os, value.index()}, value); +#endif // GTEST_HAS_ABSL + *os << ')'; + } + + private: + struct Visitor { + template + void operator()(const U& u) const { + *os << "'" << GetTypeName() << "(index = " << index + << ")' with value "; + UniversalPrint(u, os); + } + ::std::ostream* os; + std::size_t index; + }; +}; + +#endif // GTEST_INTERNAL_HAS_VARIANT + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray(const char* begin, size_t len, + ::std::ostream* os); + +#ifdef __cpp_lib_char8_t +// This overload prints a (const) char8_t array compactly. +GTEST_API_ void UniversalPrintArray(const char8_t* begin, size_t len, + ::std::ostream* os); +#endif + +// This overload prints a (const) char16_t array compactly. +GTEST_API_ void UniversalPrintArray(const char16_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) char32_t array compactly. +GTEST_API_ void UniversalPrintArray(const char32_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray(const wchar_t* begin, size_t len, + ::std::ostream* os); + +// Implements printing an array type T[N]. +template +class UniversalPrinter { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. + +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter> { + public: + static void Print(std::reference_wrapper value, ::std::ostream* os) { + UniversalTersePrinter::Print(value.get(), os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter::Print(value, os); + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(std::string(str), os); + } + } +}; +template <> +class UniversalTersePrinter : public UniversalTersePrinter { +}; + +#ifdef __cpp_lib_char8_t +template <> +class UniversalTersePrinter { + public: + static void Print(const char8_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u8string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(const char16_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u16string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; + +template <> +class UniversalTersePrinter { + public: + static void Print(const char32_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u32string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +template +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalTersePrinter::Print(value, os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template +void UniversalPrint(const T& value, ::std::ostream* os) { + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter::Print(value, os); +} + +typedef ::std::vector<::std::string> Strings; + +// Tersely prints the first N fields of a tuple to a string vector, +// one element for each field. +template +void TersePrintPrefixToStrings(const Tuple&, std::integral_constant, + Strings*) {} +template +void TersePrintPrefixToStrings(const Tuple& t, + std::integral_constant, + Strings* strings) { + TersePrintPrefixToStrings(t, std::integral_constant(), + strings); + ::std::stringstream ss; + UniversalTersePrint(std::get(t), &ss); + strings->push_back(ss.str()); +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TersePrintPrefixToStrings( + value, std::integral_constant::value>(), + &result); + return result; +} + +} // namespace internal + +template +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrinter::Print(value, &ss); + return ss.str(); +} + +} // namespace testing + +// Include any custom printer added by the local installation. +// We must include this header at the end to make sure it can use the +// declarations from this file. +#include "gtest/internal/custom/gtest-printers.h" + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest-spi.h b/Tests/ThirdParty/googletest/include/gtest/gtest-spi.h new file mode 100644 index 0000000..c0613b6 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest-spi.h @@ -0,0 +1,250 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include + +#include "gtest/gtest.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + ~ScopedFakeTestPartResultReporter() override; + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + void ReportTestPartResult(const TestPartResult& result) override; + + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + ScopedFakeTestPartResultReporter(const ScopedFakeTestPartResultReporter&) = + delete; + ScopedFakeTestPartResultReporter& operator=( + const ScopedFakeTestPartResultReporter&) = delete; +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, const std::string& substr); + ~SingleFailureChecker(); + + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const std::string substr_; + + SingleFailureChecker(const SingleFailureChecker&) = delete; + SingleFailureChecker& operator=(const SingleFailureChecker&) = delete; +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures (e.g. a failure from an ASSERT_EQ, but +// not a non-fatal failure, as from EXPECT_EQ). It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper { \ + public: \ + static void Execute() { statement; } \ + }; \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, \ + >est_failures); \ + GTestExpectFatalFailureHelper::Execute(); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper { \ + public: \ + static void Execute() { statement; } \ + }; \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures); \ + GTestExpectFatalFailureHelper::Execute(); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures (e.g. a failure from an EXPECT_EQ, +// but not from an ASSERT_EQ). It asserts that the given statement will cause +// exactly one non-fatal Google Test failure with 'substr' being part of the +// failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do { \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, \ + >est_failures); \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } \ + } \ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures); \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } \ + } \ + } while (::testing::internal::AlwaysFalse()) + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest-test-part.h b/Tests/ThirdParty/googletest/include/gtest/gtest-test-part.h new file mode 100644 index 0000000..41c8a9a --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest-test-part.h @@ -0,0 +1,192 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include +#include +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class GTEST_API_ TestPartResult { + public: + // The possible outcomes of a test part (i.e. an assertion or an + // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). + enum Type { + kSuccess, // Succeeded. + kNonFatalFailure, // Failed but the test can continue. + kFatalFailure, // Failed and the test should be terminated. + kSkip // Skipped. + }; + + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(Type a_type, const char* a_file_name, int a_line_number, + const char* a_message) + : type_(a_type), + file_name_(a_file_name == nullptr ? "" : a_file_name), + line_number_(a_line_number), + summary_(ExtractSummary(a_message)), + message_(a_message) {} + + // Gets the outcome of the test part. + Type type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { + return file_name_.empty() ? nullptr : file_name_.c_str(); + } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the summary of the failure message. + const char* summary() const { return summary_.c_str(); } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true if and only if the test part was skipped. + bool skipped() const { return type_ == kSkip; } + + // Returns true if and only if the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true if and only if the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true if and only if the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + + // Returns true if and only if the test part failed. + bool failed() const { return fatally_failed() || nonfatally_failed(); } + + private: + Type type_; + + // Gets the summary of the failure message by omitting the stack + // trace in it. + static std::string ExtractSummary(const char* message); + + // The name of the source file where the test part took place, or + // "" if the source file is unknown. + std::string file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class GTEST_API_ TestPartResultArray { + public: + TestPartResultArray() = default; + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + + private: + std::vector array_; + + TestPartResultArray(const TestPartResultArray&) = delete; + TestPartResultArray& operator=(const TestPartResultArray&) = delete; +}; + +// This interface knows how to report a test part result. +class GTEST_API_ TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() = default; + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +namespace internal { + +// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a +// statement generates new fatal failures. To do so it registers itself as the +// current test part result reporter. Besides checking if fatal failures were +// reported, it only delegates the reporting to the former result reporter. +// The original result reporter is restored in the destructor. +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +class GTEST_API_ HasNewFatalFailureHelper + : public TestPartResultReporterInterface { + public: + HasNewFatalFailureHelper(); + ~HasNewFatalFailureHelper() override; + void ReportTestPartResult(const TestPartResult& result) override; + bool has_new_fatal_failure() const { return has_new_fatal_failure_; } + + private: + bool has_new_fatal_failure_; + TestPartResultReporterInterface* original_reporter_; + + HasNewFatalFailureHelper(const HasNewFatalFailureHelper&) = delete; + HasNewFatalFailureHelper& operator=(const HasNewFatalFailureHelper&) = delete; +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest-typed-test.h b/Tests/ThirdParty/googletest/include/gtest/gtest-typed-test.h new file mode 100644 index 0000000..305b0b5 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest-typed-test.h @@ -0,0 +1,335 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// This header implements typed tests and type-parameterized tests. + +// Typed (aka type-driven) tests repeat the same test for types in a +// list. You must know which types you want to test with when writing +// typed tests. Here's how you do it: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + public: + ... + typedef std::list List; + static T shared_; + T value_; +}; + +// Next, associate a list of types with the test suite, which will be +// repeated for each type in the list. The typedef is necessary for +// the macro to parse correctly. +typedef testing::Types MyTypes; +TYPED_TEST_SUITE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_SUITE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test suite as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to the special name TypeParam to get the type + // parameter. Since we are inside a derived class template, C++ requires + // us to visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the TestFixture:: + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the "typename + // TestFixture::" prefix. + typename TestFixture::List values; + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } + +// TYPED_TEST_SUITE takes an optional third argument which allows to specify a +// class that generates custom test name suffixes based on the type. This should +// be a class which has a static template function GetName(int index) returning +// a string for each type. The provided integer index equals the index of the +// type in the provided type list. In many cases the index can be ignored. +// +// For example: +// class MyTypeNames { +// public: +// template +// static std::string GetName(int) { +// if (std::is_same()) return "char"; +// if (std::is_same()) return "int"; +// if (std::is_same()) return "unsignedInt"; +// } +// }; +// TYPED_TEST_SUITE(FooTest, MyTypes, MyTypeNames); + +#endif // 0 + +// Type-parameterized tests are abstract test patterns parameterized +// by a type. Compared with typed tests, type-parameterized tests +// allow you to define the test pattern without knowing what the type +// parameters are. The defined pattern can be instantiated with +// different types any number of times, in any number of translation +// units. +// +// If you are designing an interface or concept, you can define a +// suite of type-parameterized tests to verify properties that any +// valid implementation of the interface/concept should have. Then, +// each implementation can easily instantiate the test suite to verify +// that it conforms to the requirements, without having to write +// similar tests repeatedly. Here's an example: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + ... +}; + +// Next, declare that you will define a type-parameterized test suite +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_SUITE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test suite as you want. +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } + +// Now the tricky part: you need to register all test patterns before +// you can instantiate them. The first argument of the macro is the +// test suite name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_SUITE_P(FooTest, + DoesBlah, HasPropertyA); + +// Finally, you are free to instantiate the pattern with the types you +// want. If you put the above code in a header file, you can #include +// it in multiple C++ source files and instantiate it multiple times. +// +// To distinguish different instances of the pattern, the first +// argument to the INSTANTIATE_* macro is a prefix that will be added +// to the actual test suite name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types MyTypes; +INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int); +// +// Similar to the optional argument of TYPED_TEST_SUITE above, +// INSTANTIATE_TEST_SUITE_P takes an optional fourth argument which allows to +// generate custom names. +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes, MyTypeNames); + +#endif // 0 + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-type-util.h" + +// Implements typed tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test suite. +#define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_ + +// Expands to the name of the typedef for the NameGenerator, responsible for +// creating the suffixes of the name. +#define GTEST_NAME_GENERATOR_(TestSuiteName) \ + gtest_type_params_##TestSuiteName##_NameGenerator + +#define TYPED_TEST_SUITE(CaseName, Types, ...) \ + typedef ::testing::internal::GenerateTypeList::type \ + GTEST_TYPE_PARAMS_(CaseName); \ + typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \ + GTEST_NAME_GENERATOR_(CaseName) + +#define TYPED_TEST(CaseName, TestName) \ + static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \ + "test-name must not be empty"); \ + template \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static bool \ + gtest_##CaseName##_##TestName##_registered_ = \ + ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel, \ + GTEST_TYPE_PARAMS_( \ + CaseName)>::Register("", \ + ::testing::internal::CodeLocation( \ + __FILE__, __LINE__), \ + GTEST_STRINGIFY_(CaseName), \ + GTEST_STRINGIFY_(TestName), 0, \ + ::testing::internal::GenerateNames< \ + GTEST_NAME_GENERATOR_(CaseName), \ + GTEST_TYPE_PARAMS_(CaseName)>()); \ + template \ + void GTEST_TEST_CLASS_NAME_(CaseName, \ + TestName)::TestBody() + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE \ + static_assert(::testing::internal::TypedTestCaseIsDeprecated(), ""); \ + TYPED_TEST_SUITE +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// Implements type-parameterized tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test suite are defined in. The exact +// name of the namespace is subject to change without notice. +#define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the variable used to remember the names of +// the defined tests in the given test suite. +#define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \ + gtest_typed_test_suite_p_state_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. +// +// Expands to the name of the variable used to remember the names of +// the registered tests in the given test suite. +#define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \ + gtest_registered_test_names_##TestSuiteName##_ + +// The variables defined in the type-parameterized test macros are +// static as typically these macros are used in a .h file that can be +// #included in multiple translation units linked together. +#define TYPED_TEST_SUITE_P(SuiteName) \ + static ::testing::internal::TypedTestSuitePState \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE_P \ + static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \ + TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define TYPED_TEST_P(SuiteName, TestName) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + template \ + class TestName : public SuiteName { \ + private: \ + typedef SuiteName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static bool \ + gtest_##TestName##_defined_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \ + __FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \ + GTEST_STRINGIFY_(TestName)); \ + } \ + template \ + void GTEST_SUITE_NAMESPACE_( \ + SuiteName)::TestName::TestBody() + +// Note: this won't work correctly if the trailing arguments are macros. +#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \ + } \ + GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static const char* const \ + GTEST_REGISTERED_TEST_NAMES_(SuiteName) = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \ + GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define REGISTER_TYPED_TEST_CASE_P \ + static_assert(::testing::internal::RegisterTypedTestCase_P_IsDeprecated(), \ + ""); \ + REGISTER_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \ + static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \ + "test-suit-prefix must not be empty"); \ + GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static bool \ + gtest_##Prefix##_##SuiteName = \ + ::testing::internal::TypeParameterizedTestSuite< \ + SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \ + ::testing::internal::GenerateTypeList::type>:: \ + Register( \ + GTEST_STRINGIFY_(Prefix), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), \ + >EST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \ + GTEST_STRINGIFY_(SuiteName), \ + GTEST_REGISTERED_TEST_NAMES_(SuiteName), \ + ::testing::internal::GenerateNames< \ + ::testing::internal::NameGeneratorSelector< \ + __VA_ARGS__>::type, \ + ::testing::internal::GenerateTypeList::type>()) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TYPED_TEST_CASE_P \ + static_assert( \ + ::testing::internal::InstantiateTypedTestCase_P_IsDeprecated(), ""); \ + INSTANTIATE_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest.h b/Tests/ThirdParty/googletest/include/gtest/gtest.h new file mode 100644 index 0000000..c899669 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest.h @@ -0,0 +1,2338 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-assertion-result.h" // IWYU pragma: export +#include "gtest/gtest-death-test.h" // IWYU pragma: export +#include "gtest/gtest-matchers.h" // IWYU pragma: export +#include "gtest/gtest-message.h" // IWYU pragma: export +#include "gtest/gtest-param-test.h" // IWYU pragma: export +#include "gtest/gtest-printers.h" // IWYU pragma: export +#include "gtest/gtest-test-part.h" // IWYU pragma: export +#include "gtest/gtest-typed-test.h" // IWYU pragma: export +#include "gtest/gtest_pred_impl.h" // IWYU pragma: export +#include "gtest/gtest_prod.h" // IWYU pragma: export +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Declares the flags. + +// This flag temporary enables the disabled tests. +GTEST_DECLARE_bool_(also_run_disabled_tests); + +// This flag brings the debugger on an assertion failure. +GTEST_DECLARE_bool_(break_on_failure); + +// This flag controls whether Google Test catches all test-thrown exceptions +// and logs them as failures. +GTEST_DECLARE_bool_(catch_exceptions); + +// This flag enables using colors in terminal output. Available values are +// "yes" to enable colors, "no" (disable colors), or "auto" (the default) +// to let Google Test decide. +GTEST_DECLARE_string_(color); + +// This flag controls whether the test runner should continue execution past +// first failure. +GTEST_DECLARE_bool_(fail_fast); + +// This flag sets up the filter to select by name using a glob pattern +// the tests to run. If the filter is not given all tests are executed. +GTEST_DECLARE_string_(filter); + +// This flag controls whether Google Test installs a signal handler that dumps +// debugging information when fatal signals are raised. +GTEST_DECLARE_bool_(install_failure_signal_handler); + +// This flag causes the Google Test to list tests. None of the tests listed +// are actually run if the flag is provided. +GTEST_DECLARE_bool_(list_tests); + +// This flag controls whether Google Test emits a detailed XML report to a file +// in addition to its normal textual output. +GTEST_DECLARE_string_(output); + +// This flags control whether Google Test prints only test failures. +GTEST_DECLARE_bool_(brief); + +// This flags control whether Google Test prints the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// This flags control whether Google Test prints UTF8 characters as text. +GTEST_DECLARE_bool_(print_utf8); + +// This flag specifies the random number seed. +GTEST_DECLARE_int32_(random_seed); + +// This flag sets how many times the tests are repeated. The default value +// is 1. If the value is -1 the tests are repeating forever. +GTEST_DECLARE_int32_(repeat); + +// This flag controls whether Google Test Environments are recreated for each +// repeat of the tests. The default value is true. If set to false the global +// test Environment objects are only set up once, for the first iteration, and +// only torn down once, for the last. +GTEST_DECLARE_bool_(recreate_environments_when_repeating); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool_(show_internal_stack_frames); + +// When this flag is specified, tests' order is randomized on every iteration. +GTEST_DECLARE_bool_(shuffle); + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32_(stack_trace_depth); + +// When this flag is specified, a failed assertion will throw an +// exception if exceptions are enabled, or exit the program with a +// non-zero code otherwise. For use with an external test framework. +GTEST_DECLARE_bool_(throw_on_failure); + +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +GTEST_DECLARE_string_(flagfile); +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +namespace testing { + +// Silence C4100 (unreferenced formal parameter) and 4805 +// unsafe mix of type 'const int' and type 'const bool' +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4805 4100) + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +namespace internal { + +class AssertHelper; +class DefaultGlobalTestPartResultReporter; +class ExecDeathTest; +class NoExecDeathTest; +class FinalSuccessChecker; +class GTestFlagSaver; +class StreamingListenerTest; +class TestResultAccessor; +class TestEventListenersAccessor; +class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; +class WindowsDeathTest; +class FuchsiaDeathTest; +class UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message); +std::set* GetIgnoredParameterizedTestSuites(); + +// A base class that prevents subclasses from being copyable. +// We do this instead of using '= delete' so as to avoid triggering warnings +// inside user code regarding any of our declarations. +class GTestNonCopyable { + public: + GTestNonCopyable() = default; + GTestNonCopyable(const GTestNonCopyable&) = delete; + GTestNonCopyable& operator=(const GTestNonCopyable&) = delete; + ~GTestNonCopyable() = default; +}; + +} // namespace internal + +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestSuite; + +// Old API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TestCase = TestSuite; +#endif +class TestInfo; +class UnitTest; + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestSuites, and +// each TestSuite contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used in a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { ... } +// void TearDown() override { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Sets up the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::SetUpTestSuite() before running the first + // test in test suite Foo. Hence a sub-class can define its own + // SetUpTestSuite() method to shadow the one defined in the super + // class. + static void SetUpTestSuite() {} + + // Tears down the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::TearDownTestSuite() after running the last + // test in test suite Foo. Hence a sub-class can define its own + // TearDownTestSuite() method to shadow the one defined in the super + // class. + static void TearDownTestSuite() {} + + // Legacy API is deprecated but still available. Use SetUpTestSuite and + // TearDownTestSuite instead. +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + static void TearDownTestCase() {} + static void SetUpTestCase() {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns true if and only if the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true if and only if the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true if and only if the current test was skipped. + static bool IsSkipped(); + + // Returns true if and only if the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } + + // Logs a property for the current test, test suite, or for the entire + // invocation of the test program when used outside of the context of a + // test suite. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestSuite or TearDownTestSuite are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + // We do not define a custom serialization except for values that can be + // converted to int64_t, but other values could be logged in this way. + template ::value, + bool> = true> + static void RecordProperty(const std::string& key, const T& value) { + RecordProperty(key, (Message() << value).GetString()); + } + + protected: + // Creates a Test object. + Test(); + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true if and only if the current test has the same fixture class + // as the first test in the current test suite. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + + const std::unique_ptr gtest_flag_saver_; + + // Often a user misspells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if void Setup() is declared in the user's + // test fixture. + // + // - This method is private, so it will be another compiler error + // if the method is called from the user's test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } + + // We disallow copying Tests. + Test(const Test&) = delete; + Test& operator=(const Test&) = delete; +}; + +typedef internal::TimeInMillis TimeInMillis; + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const std::string& a_key, const std::string& a_value) + : key_(a_key), value_(a_value) {} + + // Gets the user supplied key. + const char* key() const { return key_.c_str(); } + + // Gets the user supplied value. + const char* value() const { return value_.c_str(); } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const std::string& new_value) { value_ = new_value; } + + private: + // The key supplied by the user. + std::string key_; + // The value supplied by the user. + std::string value_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns the number of the test properties. + int test_property_count() const; + + // Returns true if and only if the test passed (i.e. no test part failed). + bool Passed() const { return !Skipped() && !Failed(); } + + // Returns true if and only if the test was skipped. + bool Skipped() const; + + // Returns true if and only if the test failed. + bool Failed() const; + + // Returns true if and only if the test fatally failed. + bool HasFatalFailure() const; + + // Returns true if and only if the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test case start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test part result among all the results. i can range from 0 + // to total_part_count() - 1. If i is not in that range, aborts the program. + const TestPartResult& GetTestPartResult(int i) const; + + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; + + private: + friend class TestInfo; + friend class TestSuite; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + friend class internal::FuchsiaDeathTest; + + // Gets the vector of TestPartResults. + const std::vector& test_part_results() const { + return test_part_results_; + } + + // Gets the vector of TestProperties. + const std::vector& test_properties() const { + return test_properties_; + } + + // Sets the start time. + void set_start_timestamp(TimeInMillis start) { start_timestamp_ = start; } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testsuite tags. Returns true if the property is valid. + // FIXME: Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the test part results. + void ClearTestPartResults(); + + // Clears the object. + void Clear(); + + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properties_mutex_; + + // The vector of TestPartResults + std::vector test_part_results_; + // The vector of TestProperties + std::vector test_properties_; + // Running count of death tests. + int death_test_count_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + TestResult(const TestResult&) = delete; + TestResult& operator=(const TestResult&) = delete; +}; // class TestResult + +// A TestInfo object stores the following information about a test: +// +// Test suite name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test suite name. + const char* test_suite_name() const { return test_suite_name_.c_str(); } + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const char* test_case_name() const { return test_suite_name(); } +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_ != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_ != nullptr) return value_param_->c_str(); + return nullptr; + } + + // Returns the file name where this test is defined. + const char* file() const { return location_.file.c_str(); } + + // Returns the line where this test is defined. + int line() const { return location_.line; } + + // Return true if this test should not be run because it's in another shard. + bool is_in_another_shard() const { return is_in_another_shard_; } + + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test suite Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns true if and only if this test will appear in the XML report. + bool is_reportable() const { + // The XML report includes tests matching the filter, excluding those + // run in other shards. + return matches_filter_ && !is_in_another_shard_; + } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: +#ifdef GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestSuite; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + std::string test_suite_name, const char* name, const char* type_param, + const char* value_param, internal::CodeLocation code_location, + internal::TypeId fixture_class_id, internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(std::string test_suite_name, std::string name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test + internal::CodeLocation a_code_location, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + // Skip and records the test result for this object. + void Skip(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_suite_name_; // test suite name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const std::unique_ptr value_param_; + internal::CodeLocation location_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True if and only if this test should run + bool is_disabled_; // True if and only if this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + bool is_in_another_shard_; // Will be run in another shard. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + TestInfo(const TestInfo&) = delete; + TestInfo& operator=(const TestInfo&) = delete; +}; + +// A test suite, which consists of a vector of TestInfos. +// +// TestSuite is not copyable. +class GTEST_API_ TestSuite { + public: + // Creates a TestSuite with the given name. + // + // TestSuite does NOT have a default constructor. Always use this + // constructor to create a TestSuite object. + // + // Arguments: + // + // name: name of the test suite + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test suite + // tear_down_tc: pointer to the function that tears down the test suite + TestSuite(const std::string& name, const char* a_type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc); + + // Destructor of TestSuite. + virtual ~TestSuite(); + + // Gets the name of the TestSuite. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test suite. + const char* type_param() const { + if (type_param_ != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns true if any test in this test suite should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test suite. + int successful_test_count() const; + + // Gets the number of skipped tests in this test suite. + int skipped_test_count() const; + + // Gets the number of failed tests in this test suite. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests in this test suite. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Get the number of tests in this test suite that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test suite. + int total_test_count() const; + + // Returns true if and only if the test suite passed. + bool Passed() const { return !Failed(); } + + // Returns true if and only if the test suite failed. + bool Failed() const { + return failed_test_count() > 0 || ad_hoc_test_result().Failed(); + } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test suite start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestSuite and TearDownTestSuite. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestSuite. + std::vector& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestSuite. + const std::vector& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test suite. Will delete the TestInfo upon + // destruction of the TestSuite object. + void AddTestInfo(TestInfo* test_info); + + // Clears the results of all tests in this test suite. + void ClearResult(); + + // Clears the results of all tests in the given test suite. + static void ClearTestSuiteResult(TestSuite* test_suite) { + test_suite->ClearResult(); + } + + // Runs every test in this TestSuite. + void Run(); + + // Skips the execution of tests under this TestSuite + void Skip(); + + // Runs SetUpTestSuite() for this TestSuite. This wrapper is needed + // for catching exceptions thrown from SetUpTestSuite(). + void RunSetUpTestSuite() { + if (set_up_tc_ != nullptr) { + (*set_up_tc_)(); + } + } + + // Runs TearDownTestSuite() for this TestSuite. This wrapper is + // needed for catching exceptions thrown from TearDownTestSuite(). + void RunTearDownTestSuite() { + if (tear_down_tc_ != nullptr) { + (*tear_down_tc_)(); + } + } + + // Returns true if and only if test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true if and only if test skipped. + static bool TestSkipped(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Skipped(); + } + + // Returns true if and only if test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true if and only if the test is disabled and will be reported in + // the XML report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + + // Returns true if and only if test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true if and only if this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test suite. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test suite. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector test_indices_; + // Pointer to the function that sets up the test suite. + internal::SetUpTestSuiteFunc set_up_tc_; + // Pointer to the function that tears down the test suite. + internal::TearDownTestSuiteFunc tear_down_tc_; + // True if and only if any test in this test suite should run. + bool should_run_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestSuite and + // TearDownTestSuite. + TestResult ad_hoc_test_result_; + + // We disallow copying TestSuites. + TestSuite(const TestSuite&) = delete; + TestSuite& operator=(const TestSuite&) = delete; +}; + +// An Environment object is capable of setting up and tearing down an +// environment. You should subclass this to define your own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() = default; + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } +}; + +#if GTEST_HAS_EXCEPTIONS + +// Exception which can be thrown from TestEventListener::OnTestPartResult. +class GTEST_API_ AssertionException + : public internal::GoogleTestFailureException { + public: + explicit AssertionException(const TestPartResult& result) + : GoogleTestFailureException(result) {} +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() = default; + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test suite starts. + virtual void OnTestSuiteStart(const TestSuite& /*test_suite*/) {} + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired when a test is disabled + virtual void OnTestDisabled(const TestInfo& /*test_info*/) {} + + // Fired after a failed assertion or a SUCCEED() invocation. + // If you want to throw an exception from this function to skip to the next + // TEST, it must be AssertionException defined above, or inherited from it. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test suite ends. + virtual void OnTestSuiteEnd(const TestSuite& /*test_suite*/) {} + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before environment tear-down for each iteration of tests starts. + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; + + // Fired after environment tear-down for each iteration of tests ends. + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; + + // Fired after each iteration of tests finishes. + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration) = 0; + + // Fired after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; +}; + +// The convenience class for users who need to override just one or two +// methods and are not concerned that a possible change to a signature of +// the methods they override will not be caught during the build. For +// comments about each method please see the definition of TestEventListener +// above. +class EmptyTestEventListener : public TestEventListener { + public: + void OnTestProgramStart(const UnitTest& /*unit_test*/) override {} + void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {} + void OnTestSuiteStart(const TestSuite& /*test_suite*/) override {} +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnTestStart(const TestInfo& /*test_info*/) override {} + void OnTestDisabled(const TestInfo& /*test_info*/) override {} + void OnTestPartResult(const TestPartResult& /*test_part_result*/) override {} + void OnTestEnd(const TestInfo& /*test_info*/) override {} + void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {} +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {} + void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {} +}; + +// TestEventListeners lets users add listeners to track events in Google Test. +class GTEST_API_ TestEventListeners { + public: + TestEventListeners(); + ~TestEventListeners(); + + // Appends an event listener to the end of the list. Google Test assumes + // the ownership of the listener (i.e. it will delete the listener when + // the test program finishes). + void Append(TestEventListener* listener); + + // Removes the given event listener from the list and returns it. It then + // becomes the caller's responsibility to delete the listener. Returns + // NULL if the listener is not found in the list. + TestEventListener* Release(TestEventListener* listener); + + // Returns the standard listener responsible for the default console + // output. Can be removed from the listeners list to shut down default + // console output. Note that removing this object from the listener list + // with Release transfers its ownership to the caller and makes this + // function return NULL the next time. + TestEventListener* default_result_printer() const { + return default_result_printer_; + } + + // Returns the standard listener responsible for the default XML output + // controlled by the --gtest_output=xml flag. Can be removed from the + // listeners list by users who want to shut down the default XML output + // controlled by this flag and substitute it with custom one. Note that + // removing this object from the listener list with Release transfers its + // ownership to the caller and makes this function return NULL the next + // time. + TestEventListener* default_xml_generator() const { + return default_xml_generator_; + } + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + void SuppressEventForwarding(bool); + + private: + friend class TestSuite; + friend class TestInfo; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::NoExecDeathTest; + friend class internal::TestEventListenersAccessor; + friend class internal::UnitTestImpl; + + // Returns repeater that broadcasts the TestEventListener events to all + // subscribers. + TestEventListener* repeater(); + + // Sets the default_result_printer attribute to the provided listener. + // The listener is also added to the listener list and previous + // default_result_printer is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultResultPrinter(TestEventListener* listener); + + // Sets the default_xml_generator attribute to the provided listener. The + // listener is also added to the listener list and previous + // default_xml_generator is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultXmlGenerator(TestEventListener* listener); + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + bool EventForwardingEnabled() const; + + // The actual list of listeners. + internal::TestEventRepeater* repeater_; + // Listener responsible for the standard result output. + TestEventListener* default_result_printer_; + // Listener responsible for the creation of the XML output file. + TestEventListener* default_xml_generator_; + + // We disallow copying TestEventListeners. + TestEventListeners(const TestEventListeners&) = delete; + TestEventListeners& operator=(const TestEventListeners&) = delete; +}; + +// A UnitTest consists of a vector of TestSuites. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class GTEST_API_ UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT_; + + // Returns the working directory when the first TEST() or TEST_F() + // was executed. The UnitTest object owns the string. + const char* original_working_dir() const; + + // Returns the TestSuite object for the test that's currently running, + // or NULL if no test is running. + const TestSuite* current_test_suite() const GTEST_LOCK_EXCLUDED_(mutex_); + +// Legacy API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* current_test_case() const GTEST_LOCK_EXCLUDED_(mutex_); +#endif + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the random seed used at the start of the current test run. + int random_seed() const; + + // Returns the ParameterizedTestSuiteRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestSuiteRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Gets the number of successful test suites. + int successful_test_suite_count() const; + + // Gets the number of failed test suites. + int failed_test_suite_count() const; + + // Gets the number of all test suites. + int total_test_suite_count() const; + + // Gets the number of all test suites that contain at least one test + // that should run. + int test_suite_to_run_count() const; + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + int successful_test_case_count() const; + int failed_test_case_count() const; + int total_test_case_count() const; + int test_case_to_run_count() const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of skipped tests. + int skipped_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const; + + // Returns true if and only if the unit test passed (i.e. all test suites + // passed). + bool Passed() const; + + // Returns true if and only if the unit test failed (i.e. some test suite + // failed or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + const TestSuite* GetTestSuite(int i) const; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* GetTestCase(int i) const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test suites. + const TestResult& ad_hoc_test_result() const; + + // Returns the list of event listeners that can be used to track events + // inside Google Test. + TestEventListeners& listeners(); + + private: + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + void AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, int line_number, + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestSuite's ad_hoc_test_result_ when invoked + // from SetUpTestSuite or TearDownTestSuite, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + TestSuite* GetMutableTestSuite(int i); + + // Invokes OsStackTrackGetterInterface::UponLeavingGTest. UponLeavingGTest() + // should be called immediately before Google Test calls user code. It saves + // some information about the current stack that CurrentStackTrace() will use + // to find and hide Google Test stack frames. + void UponLeavingGTest(); + + // Sets the TestSuite object for the test that's currently running. + void set_current_test_suite(TestSuite* a_current_test_suite) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Sets the TestInfo object for the test that's currently running. + void set_current_test_info(TestInfo* a_current_test_info) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and functions are friends as they need to access private + // members of UnitTest. + friend class ScopedTrace; + friend class Test; + friend class TestInfo; + friend class TestSuite; + friend class internal::AssertHelper; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; + friend Environment* AddGlobalTestEnvironment(Environment* env); + friend std::set* internal::GetIgnoredParameterizedTestSuites(); + friend internal::UnitTestImpl* internal::GetUnitTestImpl(); + friend void internal::ReportFailureInUnknownLocation( + TestPartResult::Type result_type, const std::string& message); + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace() GTEST_LOCK_EXCLUDED_(mutex_); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + UnitTest(const UnitTest&) = delete; + UnitTest& operator=(const UnitTest&) = delete; +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +GTEST_API_ void InitGoogleTest(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); + +// This overloaded version can be used on Arduino/embedded platforms where +// there is no argc/argv. +GTEST_API_ void InitGoogleTest(); + +namespace internal { + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperEQ. This helps reduce the overhead of some sanitizers +// when calling EXPECT_* in a tight loop. +template +AssertionResult CmpHelperEQFailure(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + return EqFailure(lhs_expression, rhs_expression, + FormatForComparisonFailureMessage(lhs, rhs), + FormatForComparisonFailureMessage(rhs, lhs), false); +} + +// This block of code defines operator==/!= +// to block lexical scope lookup. +// It prevents using invalid operator==/!= defined at namespace scope. +struct faketype {}; +inline bool operator==(faketype, faketype) { return true; } +inline bool operator!=(faketype, faketype) { return false; } + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + if (lhs == rhs) { + return AssertionSuccess(); + } + + return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); +} + +class EqHelper { + public: + // This templatized version is for the general case. + template < + typename T1, typename T2, + // Disable this overload for cases where one argument is a pointer + // and the other is the null pointer constant. + typename std::enable_if::value || + !std::is_pointer::value>::type* = nullptr> + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, BiggestInt lhs, + BiggestInt rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + template + static AssertionResult Compare( + const char* lhs_expression, const char* rhs_expression, + // Handle cases where '0' is used as a null pointer literal. + std::nullptr_t /* lhs */, T* rhs) { + // We already know that 'lhs' is a null pointer. + return CmpHelperEQ(lhs_expression, rhs_expression, static_cast(nullptr), + rhs); + } +}; + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperOP. This helps reduce the overhead of some sanitizers +// when calling EXPECT_OP in a tight loop. +template +AssertionResult CmpHelperOpFailure(const char* expr1, const char* expr2, + const T1& val1, const T2& val2, + const char* op) { + return AssertionFailure() + << "Expected: (" << expr1 << ") " << op << " (" << expr2 + << "), actual: " << FormatForComparisonFailureMessage(val1, val2) + << " vs " << FormatForComparisonFailureMessage(val2, val1); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +#define GTEST_IMPL_CMP_HELPER_(op_name, op) \ + template \ + AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) { \ + if (val1 op val2) { \ + return AssertionSuccess(); \ + } else { \ + return CmpHelperOpFailure(expr1, expr2, val1, val2, #op); \ + } \ + } + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER_(LT, <) +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER_(GT, >) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, const wchar_t* s2); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const char* needle, + const char* haystack); +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const wchar_t* needle, + const wchar_t* haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const char* needle, + const char* haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const wchar_t* needle, + const wchar_t* haystack); +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::string& needle, + const ::std::string& haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::string& needle, + const ::std::string& haystack); + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::wstring& needle, + const ::std::wstring& haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::wstring& needle, + const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +AssertionResult CmpHelperFloatingPointEQ(const char* lhs_expression, + const char* rhs_expression, + RawType lhs_value, RawType rhs_value) { + const FloatingPoint lhs(lhs_value), rhs(rhs_value); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream lhs_ss; + lhs_ss.precision(std::numeric_limits::digits10 + 2); + lhs_ss << lhs_value; + + ::std::stringstream rhs_ss; + rhs_ss.precision(std::numeric_limits::digits10 + 2); + rhs_ss << rhs_value; + + return EqFailure(lhs_expression, rhs_expression, + StringStreamToString(&lhs_ss), StringStreamToString(&rhs_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class GTEST_API_ AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResult::Type type, const char* file, int line, + const char* message); + ~AssertHelper(); + + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE_ macro below. + void operator=(const Message& message) const; + + private: + // We put our data in a struct so that the size of the AssertHelper class can + // be as small as possible. This is important because gcc is incapable of + // re-using stack space even for temporary variables, so every EXPECT_EQ + // reserves stack space for another AssertHelper. + struct AssertHelperData { + AssertHelperData(TestPartResult::Type t, const char* srcfile, int line_num, + const char* msg) + : type(t), file(srcfile), line(line_num), message(msg) {} + + TestPartResult::Type const type; + const char* const file; + int const line; + std::string const message; + + private: + AssertHelperData(const AssertHelperData&) = delete; + AssertHelperData& operator=(const AssertHelperData&) = delete; + }; + + AssertHelperData* const data_; + + AssertHelper(const AssertHelper&) = delete; + AssertHelper& operator=(const AssertHelper&) = delete; +}; + +} // namespace internal + +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. +// +// This interface has support for accessing the test parameter value via +// the GetParam() method. +// +// Use it with one of the parameter generator defining functions, like Range(), +// Values(), ValuesIn(), Bool(), Combine(), and ConvertGenerator(). +// +// class FooTest : public ::testing::TestWithParam { +// protected: +// FooTest() { +// // Can use GetParam() here. +// } +// ~FooTest() override { +// // Can use GetParam() here. +// } +// void SetUp() override { +// // Can use GetParam() here. +// } +// void TearDown override { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_SUITE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); + +template +class WithParamInterface { + public: + typedef T ParamType; + virtual ~WithParamInterface() = default; + + // The current parameter value. Is also available in the test fixture's + // constructor. + static const ParamType& GetParam() { + GTEST_CHECK_(parameter_ != nullptr) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } + + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { parameter_ = parameter; } + + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; + + // TestClass must be a subclass of WithParamInterface and Test. + template + friend class internal::ParameterizedTestFactory; +}; + +template +const T* WithParamInterface::parameter_ = nullptr; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template +class TestWithParam : public Test, public WithParamInterface {}; + +// Macros for indicating success/failure in test code. + +// Skips test in runtime. +// Skipping test aborts current function. +// Skipped tests are neither successful nor failed. +#define GTEST_SKIP() GTEST_SKIP_("") + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") + +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") + +// Like GTEST_FAIL(), but at the given source file location. +#define GTEST_FAIL_AT(file, line) \ + return GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kFatalFailure) + +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !(defined(GTEST_DONT_DEFINE_FAIL) && GTEST_DONT_DEFINE_FAIL) +#define FAIL() GTEST_FAIL() +#define FAIL_AT(file, line) GTEST_FAIL_AT(file, line) +#endif + +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") + +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !(defined(GTEST_DONT_DEFINE_SUCCEED) && GTEST_DONT_DEFINE_SUCCEED) +#define SUCCEED() GTEST_SUCCEED() +#endif + +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. + +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) + +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define GTEST_EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, GTEST_FATAL_FAILURE_) +#define GTEST_ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Define these macros to 1 to omit the definition of the corresponding +// EXPECT or ASSERT, which clashes with some users' own code. + +#if !(defined(GTEST_DONT_DEFINE_EXPECT_TRUE) && GTEST_DONT_DEFINE_EXPECT_TRUE) +#define EXPECT_TRUE(condition) GTEST_EXPECT_TRUE(condition) +#endif + +#if !(defined(GTEST_DONT_DEFINE_EXPECT_FALSE) && GTEST_DONT_DEFINE_EXPECT_FALSE) +#define EXPECT_FALSE(condition) GTEST_EXPECT_FALSE(condition) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_TRUE) && GTEST_DONT_DEFINE_ASSERT_TRUE) +#define ASSERT_TRUE(condition) GTEST_ASSERT_TRUE(condition) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_FALSE) && GTEST_DONT_DEFINE_ASSERT_FALSE) +#define ASSERT_FALSE(condition) GTEST_ASSERT_FALSE(condition) +#endif + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2 +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(v1, v2) is preferred to +// {ASSERT|EXPECT}_TRUE(v1 == v2), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(Foo(), 5); +// EXPECT_EQ(a_pointer, NULL); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define EXPECT_NE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define GTEST_ASSERT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define GTEST_ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define GTEST_ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define GTEST_ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define GTEST_ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define GTEST_ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_EQ) && GTEST_DONT_DEFINE_ASSERT_EQ) +#define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_NE) && GTEST_DONT_DEFINE_ASSERT_NE) +#define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_LE) && GTEST_DONT_DEFINE_ASSERT_LE) +#define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_LT) && GTEST_DONT_DEFINE_ASSERT_LT) +#define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_GE) && GTEST_DONT_DEFINE_ASSERT_GE) +#define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !(defined(GTEST_DONT_DEFINE_ASSERT_GT) && GTEST_DONT_DEFINE_ASSERT_GT) +#define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + +// C-string Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define EXPECT_STRCASENE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define ASSERT_STRCASENE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(val1, val2): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(val1, val2): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_DOUBLE_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_FLOAT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_DOUBLE_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_NEAR(val1, val2, abs_error) \ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, val1, val2, \ + abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error) \ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, val1, val2, \ + abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + +#ifdef GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. +#define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +#define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +#define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); +// +// Examples: +// +// EXPECT_NO_FATAL_FAILURE(Process()); +// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; +// +#define ASSERT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) +#define EXPECT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) + +// Causes a trace (including the given source file path and line number, +// and the given message) to be included in every test failure message generated +// by code in the scope of the lifetime of an instance of this class. The effect +// is undone with the destruction of the instance. +// +// The message argument can be anything streamable to std::ostream. +// +// Example: +// testing::ScopedTrace trace("file.cc", 123, "message"); +// +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + + // Template version. Uses Message() to convert the values into strings. + // Slow, but flexible. + template + ScopedTrace(const char* file, int line, const T& message) { + PushTrace(file, line, (Message() << message).GetString()); + } + + // Optimize for some known types. + ScopedTrace(const char* file, int line, const char* message) { + PushTrace(file, line, message ? message : "(null)"); + } + + ScopedTrace(const char* file, int line, const std::string& message) { + PushTrace(file, line, message); + } + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + void PushTrace(const char* file, int line, std::string message); + + ScopedTrace(const ScopedTrace&) = delete; + ScopedTrace& operator=(const ScopedTrace&) = delete; +}; + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +// +// Assuming that each thread maintains its own stack of traces. +// Therefore, a SCOPED_TRACE() would (correctly) only affect the +// assertions in its own thread. +#define SCOPED_TRACE(message) \ + const ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)( \ + __FILE__, __LINE__, (message)) + +// Compile-time assertion for type equality. +// StaticAssertTypeEq() compiles if and only if type1 and type2 +// are the same type. The value it returns is not interesting. +// +// Instead of making StaticAssertTypeEq a class template, we make it a +// function template that invokes a helper class template. This +// prevents a user from misusing StaticAssertTypeEq by +// defining objects of that type. +// +// CAVEAT: +// +// When used inside a method of a class template, +// StaticAssertTypeEq() is effective ONLY IF the method is +// instantiated. For example, given: +// +// template class Foo { +// public: +// void Bar() { testing::StaticAssertTypeEq(); } +// }; +// +// the code: +// +// void Test1() { Foo foo; } +// +// will NOT generate a compiler error, as Foo::Bar() is never +// actually instantiated. Instead, you need: +// +// void Test2() { Foo foo; foo.Bar(); } +// +// to cause a compiler error. +template +constexpr bool StaticAssertTypeEq() noexcept { + static_assert(std::is_same::value, "T1 and T2 are not the same type"); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test suite, and the second +// parameter is the name of the test within the test suite. +// +// The convention is to end the test suite name with "Test". For +// example, a test suite for the Foo class can be named FooTest. +// +// Test code should appear between braces after an invocation of +// this macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +// Note that we call GetTestTypeId() instead of GetTypeId< +// ::testing::Test>() here to get the type ID of testing::Test. This +// is to work around a suspected linker bug when using Google Test as +// a framework on Mac OS X. The bug causes GetTypeId< +// ::testing::Test>() to return different values depending on whether +// the call is from the Google Test framework itself or from user test +// code. GetTestTypeId() is guaranteed to always return the same +// value, as it always calls GetTypeId<>() from the Google Test +// framework. +#define GTEST_TEST(test_suite_name, test_name) \ + GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \ + ::testing::internal::GetTestTypeId()) + +// Define this macro to 1 to omit the definition of TEST(), which +// is a generic name and clashes with some other libraries. +#if !(defined(GTEST_DONT_DEFINE_TEST) && GTEST_DONT_DEFINE_TEST) +#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name) +#endif + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test suite name. The second parameter is the +// name of the test within the test suite. +// +// A test fixture class must be declared earlier. The user should put +// the test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(a_.size(), 0); +// EXPECT_EQ(b_.size(), 1); +// } +#define GTEST_TEST_F(test_fixture, test_name) \ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId()) +#if !(defined(GTEST_DONT_DEFINE_TEST_F) && GTEST_DONT_DEFINE_TEST_F) +#define TEST_F(test_fixture, test_name) GTEST_TEST_F(test_fixture, test_name) +#endif + +// Returns a path to a temporary directory, which should be writable. It is +// implementation-dependent whether or not the path is terminated by the +// directory-separator character. +GTEST_API_ std::string TempDir(); + +// Returns a path to a directory that contains ancillary data files that might +// be used by tests. It is implementation dependent whether or not the path is +// terminated by the directory-separator character. The directory and the files +// in it should be considered read-only. +GTEST_API_ std::string SrcDir(); + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4805 4100 + +// Dynamically registers a test with the framework. +// +// This is an advanced API only to be used when the `TEST` macros are +// insufficient. The macros should be preferred when possible, as they avoid +// most of the complexity of calling this function. +// +// The `factory` argument is a factory callable (move-constructible) object or +// function pointer that creates a new instance of the Test object. It +// handles ownership to the caller. The signature of the callable is +// `Fixture*()`, where `Fixture` is the test fixture class for the test. All +// tests registered with the same `test_suite_name` must return the same +// fixture type. This is checked at runtime. +// +// The framework will infer the fixture class from the factory and will call +// the `SetUpTestSuite` and `TearDownTestSuite` for it. +// +// Must be called before `RUN_ALL_TESTS()` is invoked, otherwise behavior is +// undefined. +// +// Use case example: +// +// class MyFixture : public ::testing::Test { +// public: +// // All of these optional, just like in regular macro usage. +// static void SetUpTestSuite() { ... } +// static void TearDownTestSuite() { ... } +// void SetUp() override { ... } +// void TearDown() override { ... } +// }; +// +// class MyTest : public MyFixture { +// public: +// explicit MyTest(int data) : data_(data) {} +// void TestBody() override { ... } +// +// private: +// int data_; +// }; +// +// void RegisterMyTests(const std::vector& values) { +// for (int v : values) { +// ::testing::RegisterTest( +// "MyFixture", ("Test" + std::to_string(v)).c_str(), nullptr, +// std::to_string(v).c_str(), +// __FILE__, __LINE__, +// // Important to use the fixture type as the return type here. +// [=]() -> MyFixture* { return new MyTest(v); }); +// } +// } +// ... +// int main(int argc, char** argv) { +// ::testing::InitGoogleTest(&argc, argv); +// std::vector values_to_test = LoadValuesFromConfig(); +// RegisterMyTests(values_to_test); +// ... +// return RUN_ALL_TESTS(); +// } +// +template +TestInfo* RegisterTest(const char* test_suite_name, const char* test_name, + const char* type_param, const char* value_param, + const char* file, int line, Factory factory) { + using TestT = typename std::remove_pointer::type; + + class FactoryImpl : public internal::TestFactoryBase { + public: + explicit FactoryImpl(Factory f) : factory_(std::move(f)) {} + Test* CreateTest() override { return factory_(); } + + private: + Factory factory_; + }; + + return internal::MakeAndRegisterTestInfo( + test_suite_name, test_name, type_param, value_param, + internal::CodeLocation(file, line), internal::GetTypeId(), + internal::SuiteApiResolver::GetSetUpCaseOrSuite(file, line), + internal::SuiteApiResolver::GetTearDownCaseOrSuite(file, line), + new FactoryImpl{std::move(factory)}); +} + +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by InitGoogleTest(). RUN_ALL_TESTS will tear down and delete any +// installed environments and should only be called once per binary. +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; + +inline int RUN_ALL_TESTS() { return ::testing::UnitTest::GetInstance()->Run(); } + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest_pred_impl.h b/Tests/ThirdParty/googletest/include/gtest/gtest_pred_impl.h new file mode 100644 index 0000000..47a24aa --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest_pred_impl.h @@ -0,0 +1,279 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Implements a family of generic predicate assertion macros. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +#include "gtest/gtest-assertion-result.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, const char* e1, + Pred pred, const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, v1), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, #v1, pred, v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, const char* e1, + const char* e2, Pred pred, const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, #v1, #v2, pred, v1, v2), \ + on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, const char* e1, + const char* e2, const char* e3, Pred pred, + const T1& v1, const T2& v2, const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure) \ + GTEST_ASSERT_( \ + ::testing::AssertPred3Helper(#pred, #v1, #v2, #v3, pred, v1, v2, v3), \ + on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, const char* e1, + const char* e2, const char* e3, + const char* e4, Pred pred, const T1& v1, + const T2& v2, const T3& v3, const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, #v1, #v2, #v3, #v4, pred, \ + v1, v2, v3, v4), \ + on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, const char* e1, + const char* e2, const char* e3, + const char* e4, const char* e5, Pred pred, + const T1& v1, const T2& v2, const T3& v3, + const T4& v4, const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ", " << e5 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4) << "\n" + << e5 << " evaluates to " << ::testing::PrintToString(v5); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, #v1, #v2, #v3, #v4, #v5, \ + pred, v1, v2, v3, v4, v5), \ + on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/gtest_prod.h b/Tests/ThirdParty/googletest/include/gtest/gtest_prod.h new file mode 100644 index 0000000..1f37dc3 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/gtest_prod.h @@ -0,0 +1,60 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google C++ Testing and Mocking Framework definitions useful in production +// code. + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void PrivateMethod(); +// FRIEND_TEST(MyClassTest, PrivateMethodWorks); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, PrivateMethodWorks) { +// // Can call MyClass::PrivateMethod() here. +// } +// +// Note: The test class must be in the same namespace as the class being tested. +// For example, putting MyClassTest in an anonymous namespace will not work. + +#define FRIEND_TEST(test_case_name, test_name) \ + friend class test_case_name##_##test_name##_Test + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/custom/README.md b/Tests/ThirdParty/googletest/include/gtest/internal/custom/README.md new file mode 100644 index 0000000..cb49e2c --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/custom/README.md @@ -0,0 +1,44 @@ +# Customization Points + +The custom directory is an injection point for custom user configurations. + +## Header `gtest.h` + +### The following macros can be defined: + +* `GTEST_OS_STACK_TRACE_GETTER_` - The name of an implementation of + `OsStackTraceGetterInterface`. +* `GTEST_CUSTOM_TEMPDIR_FUNCTION_` - An override for `testing::TempDir()`. See + `testing::TempDir` for semantics and signature. + +## Header `gtest-port.h` + +The following macros can be defined: + +### Logging: + +* `GTEST_LOG_(severity)` +* `GTEST_CHECK_(condition)` +* Functions `LogToStderr()` and `FlushInfoLog()` have to be provided too. + +### Threading: + +* `GTEST_HAS_NOTIFICATION_` - Enabled if Notification is already provided. +* `GTEST_HAS_MUTEX_AND_THREAD_LOCAL_` - Enabled if `Mutex` and `ThreadLocal` + are already provided. Must also provide `GTEST_DECLARE_STATIC_MUTEX_(mutex)` + and `GTEST_DEFINE_STATIC_MUTEX_(mutex)` +* `GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)` +* `GTEST_LOCK_EXCLUDED_(locks)` + +### Underlying library support features + +* `GTEST_HAS_CXXABI_H_` + +### Exporting API symbols: + +* `GTEST_API_` - Specifier for exported symbols. + +## Header `gtest-printers.h` + +* See documentation at `gtest/gtest-printers.h` for details on how to define a + custom printer. diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/custom/gtest-port.h b/Tests/ThirdParty/googletest/include/gtest/internal/custom/gtest-port.h new file mode 100644 index 0000000..db02881 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/custom/gtest-port.h @@ -0,0 +1,37 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/custom/gtest-printers.h b/Tests/ThirdParty/googletest/include/gtest/internal/custom/gtest-printers.h new file mode 100644 index 0000000..b9495d8 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/custom/gtest-printers.h @@ -0,0 +1,42 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file provides an injection point for custom printers in a local +// installation of gTest. +// It will be included from gtest-printers.h and the overrides in this file +// will be visible to everyone. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/custom/gtest.h b/Tests/ThirdParty/googletest/include/gtest/internal/custom/gtest.h new file mode 100644 index 0000000..afaaf17 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/custom/gtest.h @@ -0,0 +1,37 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/gtest-death-test-internal.h b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-death-test-internal.h new file mode 100644 index 0000000..b363259 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-death-test-internal.h @@ -0,0 +1,306 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +#include + +#include +#include + +#include "gtest/gtest-matchers.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +GTEST_DECLARE_string_(internal_run_death_test); + +namespace testing { +namespace internal { + +// Name of the flag (needed for parsing Google Test flag). +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads +// and interpreted as a regex (rather than an Eq matcher) for legacy +// compatibility. +inline Matcher MakeDeathTestMatcher( + ::testing::internal::RE regex) { + return ContainsRegex(regex.pattern()); +} +inline Matcher MakeDeathTestMatcher(const char* regex) { + return ContainsRegex(regex); +} +inline Matcher MakeDeathTestMatcher( + const ::std::string& regex) { + return ContainsRegex(regex); +} + +// If a Matcher is passed to EXPECT_DEATH (etc.), it's +// used directly. +inline Matcher MakeDeathTestMatcher( + Matcher matcher) { + return matcher; +} + +#ifdef GTEST_HAS_DEATH_TEST + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _Exit(2), or +// returned from main() +class GTEST_API_ DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, Matcher matcher, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() = default; + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) {} + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + + private: + DeathTest* const test_; + ReturnSentinel(const ReturnSentinel&) = delete; + ReturnSentinel& operator=(const ReturnSentinel&) = delete; + }; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + static void set_last_death_test_message(const std::string& message); + + private: + // A string containing a description of the outcome of the last death test. + static std::string last_death_test_message_; + + DeathTest(const DeathTest&) = delete; + DeathTest& operator=(const DeathTest&) = delete; +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() = default; + virtual bool Create(const char* statement, + Matcher matcher, const char* file, + int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + bool Create(const char* statement, Matcher matcher, + const char* file, int line, DeathTest** test) override; +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +GTEST_API_ bool ExitedUnsuccessfully(int exit_status); + +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +#if GTEST_HAS_EXCEPTIONS +#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf( \ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +#else +#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +#endif + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +#define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create( \ + #statement, \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher), \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != nullptr) { \ + std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + const ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \ + gtest_dt); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__) \ + : fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed and the macro +// must accept a streamed message even though the message is never printed. +// The regex object is not evaluated, but it is used to prevent "unused" +// warnings and to avoid an expression that doesn't compile in debug mode. +#define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else if (!::testing::internal::AlwaysTrue()) { \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \ + } else \ + ::testing::Message() + +// A class representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +class InternalRunDeathTestFlag { + public: + InternalRunDeathTestFlag(const std::string& a_file, int a_line, int an_index, + int a_write_fd) + : file_(a_file), line_(a_line), index_(an_index), write_fd_(a_write_fd) {} + + ~InternalRunDeathTestFlag() { + if (write_fd_ >= 0) posix::Close(write_fd_); + } + + const std::string& file() const { return file_; } + int line() const { return line_; } + int index() const { return index_; } + int write_fd() const { return write_fd_; } + + private: + std::string file_; + int line_; + int index_; + int write_fd_; + + InternalRunDeathTestFlag(const InternalRunDeathTestFlag&) = delete; + InternalRunDeathTestFlag& operator=(const InternalRunDeathTestFlag&) = delete; +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/gtest-filepath.h b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-filepath.h new file mode 100644 index 0000000..6dc47be --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-filepath.h @@ -0,0 +1,233 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in gtest/internal/gtest-internal.h. +// Do not include this header file separately! + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + +#include +#include + +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-string.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +#if GTEST_HAS_FILE_SYSTEM + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class GTEST_API_ FilePath { + public: + FilePath() : pathname_("") {} + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) {} + FilePath(FilePath&& rhs) noexcept : pathname_(std::move(rhs.pathname_)) {} + + explicit FilePath(std::string pathname) : pathname_(std::move(pathname)) { + Normalize(); + } + + FilePath& operator=(const FilePath& rhs) { + Set(rhs); + return *this; + } + FilePath& operator=(FilePath&& rhs) noexcept { + pathname_ = std::move(rhs.pathname_); + return *this; + } + + void Set(const FilePath& rhs) { pathname_ = rhs.pathname_; } + + const std::string& string() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Returns the current working directory, or "" if unsuccessful. + static FilePath GetCurrentDir(); + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, int number, + const char* extension); + + // Given directory = "dir", relative_path = "test.xml", + // returns "dir/test.xml". + // On Windows, uses \ as the separator rather than /. + static FilePath ConcatPaths(const FilePath& directory, + const FilePath& relative_path); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // Returns true if and only if the path is "". + bool IsEmpty() const { return pathname_.empty(); } + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + // Returns true if pathname describes a root directory. (Windows has one + // root directory per disk drive.) + bool IsRootDirectory() const; + + // Returns true if pathname describes an absolute path. + bool IsAbsolutePath() const; + + private: + // Replaces multiple consecutive separators with a single separator. + // For example, "bar///foo" becomes "bar/foo". Does not eliminate other + // redundancies that might be in a pathname involving "." or "..". + // + // A pathname with multiple consecutive separators may occur either through + // user error or as a result of some scripts or APIs that generate a pathname + // with a trailing separator. On other platforms the same API or script + // may NOT generate a pathname with a trailing "/". Then elsewhere that + // pathname may have another "/" and pathname components added to it, + // without checking for the separator already being there. + // The script language and operating system may allow paths like "foo//bar" + // but some of the functions in FilePath will not handle that correctly. In + // particular, RemoveTrailingPathSeparator() only removes one separator, and + // it is called in CreateDirectoriesRecursively() assuming that it will change + // a pathname from directory syntax (trailing separator) to filename syntax. + // + // On Windows this method also replaces the alternate path separator '/' with + // the primary path separator '\\', so that for example "bar\\/\\foo" becomes + // "bar\\foo". + + void Normalize(); + + // Returns a pointer to the last occurrence of a valid path separator in + // the FilePath. On Windows, for example, both '/' and '\' are valid path + // separators. Returns NULL if no path separator was found. + const char* FindLastPathSeparator() const; + + // Returns the length of the path root, including the directory separator at + // the end of the prefix. Returns zero by definition if the path is relative. + // Examples: + // - [Windows] "..\Sibling" => 0 + // - [Windows] "\Windows" => 1 + // - [Windows] "C:/Windows\Notepad.exe" => 3 + // - [Windows] "\\Host\Share\C$/Windows" => 13 + // - [UNIX] "/bin" => 1 + size_t CalculateRootLength() const; + + std::string pathname_; +}; // class FilePath + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GTEST_HAS_FILE_SYSTEM + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/gtest-internal.h b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-internal.h new file mode 100644 index 0000000..7e55dc6 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-internal.h @@ -0,0 +1,1521 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +#include "gtest/internal/gtest-port.h" + +#ifdef GTEST_OS_LINUX +#include +#include +#include +#include +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-filepath.h" +#include "gtest/internal/gtest-string.h" +#include "gtest/internal/gtest-type-util.h" + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// https://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo##bar + +// Stringifies its argument. +// Work around a bug in visual studio which doesn't accept code like this: +// +// #define GTEST_STRINGIFY_(name) #name +// #define MACRO(a, b, c) ... GTEST_STRINGIFY_(a) ... +// MACRO(, x, y) +// +// Complaining about the argument to GTEST_STRINGIFY_ being empty. +// This is allowed by the spec. +#define GTEST_STRINGIFY_HELPER_(name, ...) #name +#define GTEST_STRINGIFY_(...) GTEST_STRINGIFY_HELPER_(__VA_ARGS__, ) + +namespace proto2 { +class MessageLite; +} + +namespace testing { + +// Forward declarations. + +class AssertionResult; // Result of an assertion. +class Message; // Represents a failure message. +class Test; // Represents a test. +class TestInfo; // Information about a test. +class TestPartResult; // Result of a test part. +class UnitTest; // A collection of test suites. + +template +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// An IgnoredValue object can be implicitly constructed from ANY value. +class IgnoredValue { + struct Sink {}; + + public: + // This constructor template allows any value to be implicitly + // converted to IgnoredValue. The object has no data member and + // doesn't try to remember anything about the argument. We + // deliberately omit the 'explicit' keyword in order to allow the + // conversion to be implicit. + // Disable the conversion if T already has a magical conversion operator. + // Otherwise we get ambiguity. + template ::value, + int>::type = 0> + IgnoredValue(const T& /* ignored */) {} // NOLINT(runtime/explicit) +}; + +// Appends the user-supplied message to the Google-Test-generated message. +GTEST_API_ std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +GTEST_DISABLE_MSC_WARNINGS_PUSH_( + 4275 /* an exported class was derived from a class that was not exported */) + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4275 + +#endif // GTEST_HAS_EXCEPTIONS + +namespace edit_distance { +// Returns the optimal edits to go from 'left' to 'right'. +// All edits cost the same, with replace having lower priority than +// add/remove. +// Simple implementation of the Wagner-Fischer algorithm. +// See https://en.wikipedia.org/wiki/Wagner-Fischer_algorithm +enum EditType { kMatch, kAdd, kRemove, kReplace }; +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, const std::vector& right); + +// Same as above, but the input is represented as strings. +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, + const std::vector& right); + +// Create a diff of the input strings in Unified diff format. +GTEST_API_ std::string CreateUnifiedDiff(const std::vector& left, + const std::vector& right, + size_t context = 2); + +} // namespace edit_distance + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true if and only if the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +GTEST_API_ AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case); + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +GTEST_API_ std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, const char* expression_text, + const char* actual_predicate_value, const char* expected_predicate_value); + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// https://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8 * sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = ~static_cast(0) >> + (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + static const uint32_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { return ReinterpretBits(kExponentBitMask); } + + // Non-static methods + + // Returns the bits that represents this number. + const Bits& bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true if and only if this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true if and only if this number is at most kMaxUlps ULP's away + // from rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) <= + kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read https://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits& sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits& sam1, + const Bits& sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + FloatingPointUnion u_; +}; + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint Float; +typedef FloatingPoint Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test suite, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef const void* TypeId; + +template +class TypeIdHelper { + public: + // dummy_ must not have a const type. Otherwise an overly eager + // compiler (e.g. MSVC 7.1 & 8.0) may try to merge + // TypeIdHelper::dummy_ for different Ts as an "optimization". + static bool dummy_; +}; + +template +bool TypeIdHelper::dummy_ = false; + +// GetTypeId() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template +TypeId GetTypeId() { + // The compiler is required to allocate a different + // TypeIdHelper::dummy_ variable for each T used to instantiate + // the template. Therefore, the address of dummy_ is guaranteed to + // be unique. + return &(TypeIdHelper::dummy_); +} + +// Returns the type ID of ::testing::Test. Always call this instead +// of GetTypeId< ::testing::Test>() to get the type ID of +// ::testing::Test, as the latter may give the wrong result due to a +// suspected linker bug when compiling Google Test as a Mac OS X +// framework. +GTEST_API_ TypeId GetTestTypeId(); + +// Defines the abstract factory interface that creates instances +// of a Test object. +class TestFactoryBase { + public: + virtual ~TestFactoryBase() = default; + + // Creates a test instance to run. The instance is both created and destroyed + // within TestInfoImpl::Run() + virtual Test* CreateTest() = 0; + + protected: + TestFactoryBase() {} + + private: + TestFactoryBase(const TestFactoryBase&) = delete; + TestFactoryBase& operator=(const TestFactoryBase&) = delete; +}; + +// This class provides implementation of TestFactoryBase interface. +// It is used in TEST and TEST_F macros. +template +class TestFactoryImpl : public TestFactoryBase { + public: + Test* CreateTest() override { return new TestClass; } +}; + +#ifdef GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, + long hr); // NOLINT +GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, + long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +// Types of SetUpTestSuite() and TearDownTestSuite() functions. +using SetUpTestSuiteFunc = void (*)(); +using TearDownTestSuiteFunc = void (*)(); + +struct CodeLocation { + CodeLocation(std::string a_file, int a_line) + : file(std::move(a_file)), line(a_line) {} + + std::string file; + int line; +}; + +// Helper to identify which setup function for TestCase / TestSuite to call. +// Only one function is allowed, either TestCase or TestSute but not both. + +// Utility functions to help SuiteApiResolver +using SetUpTearDownSuiteFuncType = void (*)(); + +inline SetUpTearDownSuiteFuncType GetNotDefaultOrNull( + SetUpTearDownSuiteFuncType a, SetUpTearDownSuiteFuncType def) { + return a == def ? nullptr : a; +} + +template +// Note that SuiteApiResolver inherits from T because +// SetUpTestSuite()/TearDownTestSuite() could be protected. This way +// SuiteApiResolver can access them. +struct SuiteApiResolver : T { + // testing::Test is only forward declared at this point. So we make it a + // dependent class for the compiler to be OK with it. + using Test = + typename std::conditional::type; + + static SetUpTearDownSuiteFuncType GetSetUpCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::SetUpTestCase, &Test::SetUpTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::SetUpTestSuite, &Test::SetUpTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both SetUpTestSuite and SetUpTestCase, please " + "make sure there is only one present at " + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::SetUpTestSuite; +#endif + } + + static SetUpTearDownSuiteFuncType GetTearDownCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::TearDownTestCase, &Test::TearDownTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::TearDownTestSuite, &Test::TearDownTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both TearDownTestSuite and TearDownTestCase," + " please make sure there is only one present at" + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::TearDownTestSuite; +#endif + } +}; + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_suite_name: name of the test suite +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a value-parameterized test. +// code_location: code location where the test is defined +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +GTEST_API_ TestInfo* MakeAndRegisterTestInfo( + std::string test_suite_name, const char* name, const char* type_param, + const char* value_param, CodeLocation code_location, + TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc, + TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory); + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// State of the definition of a type-parameterized test suite. +class GTEST_API_ TypedTestSuitePState { + public: + TypedTestSuitePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test suite hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, + "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_SUITE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + registered_tests_.emplace(test_name, CodeLocation(file, line)); + return true; + } + + bool TestExists(const std::string& test_name) const { + return registered_tests_.count(test_name) > 0; + } + + const CodeLocation& GetCodeLocation(const std::string& test_name) const { + RegisteredTestsMap::const_iterator it = registered_tests_.find(test_name); + GTEST_CHECK_(it != registered_tests_.end()); + return it->second; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames(const char* test_suite_name, + const char* file, int line, + const char* registered_tests); + + private: + typedef ::std::map> RegisteredTestsMap; + + bool registered_; + RegisteredTestsMap registered_tests_; +}; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TypedTestCasePState = TypedTestSuitePState; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == nullptr) { + return nullptr; + } + while (IsSpace(*(++comma))) { + } + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline std::string GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == nullptr ? str : std::string(str, comma); +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. +void SplitString(const ::std::string& str, char delimiter, + ::std::vector<::std::string>* dest); + +// The default argument to the template below for the case when the user does +// not provide a name generator. +struct DefaultNameGenerator { + template + static std::string GetName(int i) { + return StreamableToString(i); + } +}; + +template +struct NameGeneratorSelector { + typedef Provided type; +}; + +template +void GenerateNamesRecursively(internal::None, std::vector*, int) {} + +template +void GenerateNamesRecursively(Types, std::vector* result, int i) { + result->push_back(NameGenerator::template GetName(i)); + GenerateNamesRecursively(typename Types::Tail(), result, + i + 1); +} + +template +std::vector GenerateNames() { + std::vector result; + GenerateNamesRecursively(Types(), &result, 0); + return result; +} + +// TypeParameterizedTest::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, TestSuite, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, CodeLocation code_location, + const char* case_name, const char* test_names, int index, + const std::vector& type_names = + GenerateNames()) { + typedef typename Types::Head Type; + typedef Fixture FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + + "/" + type_names[static_cast(index)]), + StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(), + GetTypeName().c_str(), + nullptr, // No value parameter. + code_location, GetTypeId(), + SuiteApiResolver::GetSetUpCaseOrSuite( + code_location.file.c_str(), code_location.line), + SuiteApiResolver::GetTearDownCaseOrSuite( + code_location.file.c_str(), code_location.line), + new TestFactoryImpl); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest:: + Register(prefix, std::move(code_location), case_name, test_names, + index + 1, type_names); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTest { + public: + static bool Register(const char* /*prefix*/, CodeLocation, + const char* /*case_name*/, const char* /*test_names*/, + int /*index*/, + const std::vector& = + std::vector() /*type_names*/) { + return true; + } +}; + +GTEST_API_ void RegisterTypeParameterizedTestSuite(const char* test_suite_name, + CodeLocation code_location); +GTEST_API_ void RegisterTypeParameterizedTestSuiteInstantiation( + const char* case_name); + +// TypeParameterizedTestSuite::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template +class TypeParameterizedTestSuite { + public: + static bool Register(const char* prefix, CodeLocation code_location, + const TypedTestSuitePState* state, const char* case_name, + const char* test_names, + const std::vector& type_names = + GenerateNames()) { + RegisterTypeParameterizedTestSuiteInstantiation(case_name); + std::string test_name = + StripTrailingSpaces(GetPrefixUntilComma(test_names)); + if (!state->TestExists(test_name)) { + fprintf(stderr, "Failed to get code location for test %s.%s at %s.", + case_name, test_name.c_str(), + FormatFileLocation(code_location.file.c_str(), code_location.line) + .c_str()); + fflush(stderr); + posix::Abort(); + } + const CodeLocation& test_location = state->GetCodeLocation(test_name); + + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest::Register( + prefix, test_location, case_name, test_names, 0, type_names); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestSuite::Register(prefix, + std::move(code_location), + state, case_name, + SkipComma(test_names), + type_names); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTestSuite { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const TypedTestSuitePState* /*state*/, + const char* /*case_name*/, const char* /*test_names*/, + const std::vector& = + std::vector() /*type_names*/) { + return true; + } +}; + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop(int skip_count); + +// Helpers for suppressing warnings on unreachable code or constant +// condition. + +// Always returns true. +GTEST_API_ bool AlwaysTrue(); + +// Always returns false. +inline bool AlwaysFalse() { return !AlwaysTrue(); } + +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + +// Helper for declaring std::string within 'if' statement +// in pre C++17 build environment. +struct TrueWithString { + TrueWithString() = default; + explicit TrueWithString(const char* str) : value(str) {} + explicit TrueWithString(const std::string& str) : value(str) {} + explicit operator bool() const { return true; } + std::string value; +}; + +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class GTEST_API_ Random { + public: + static const uint32_t kMaxRange = 1u << 31; + + explicit Random(uint32_t seed) : state_(seed) {} + + void Reseed(uint32_t seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + uint32_t Generate(uint32_t range); + + private: + uint32_t state_; + Random(const Random&) = delete; + Random& operator=(const Random&) = delete; +}; + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + typename std::remove_const::type>::type + +// HasDebugStringAndShortDebugString::value is a compile-time bool constant +// that's true if and only if T has methods DebugString() and ShortDebugString() +// that return std::string. +template +class HasDebugStringAndShortDebugString { + private: + template + static auto CheckDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().DebugString())>::type; + template + static std::false_type CheckDebugString(...); + + template + static auto CheckShortDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().ShortDebugString())>::type; + template + static std::false_type CheckShortDebugString(...); + + using HasDebugStringType = decltype(CheckDebugString(nullptr)); + using HasShortDebugStringType = decltype(CheckShortDebugString(nullptr)); + + public: + static constexpr bool value = + HasDebugStringType::value && HasShortDebugStringType::value; +}; + +#ifdef GTEST_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +template +constexpr bool HasDebugStringAndShortDebugString::value; +#endif + +// When the compiler sees expression IsContainerTest(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest(0). +// The value of the expression is insignificant. +// +// In C++11 mode we check the existence of a const_iterator and that an +// iterator is properly implemented for the container. +// +// For pre-C++11 that we look for both C::iterator and C::const_iterator. +// The reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +template ().begin()), + class = decltype(::std::declval().end()), + class = decltype(++::std::declval()), + class = decltype(*::std::declval()), + class = typename C::const_iterator> +IsContainer IsContainerTest(int /* dummy */) { + return 0; +} + +typedef char IsNotContainer; +template +IsNotContainer IsContainerTest(long /* dummy */) { + return '\0'; +} + +// Trait to detect whether a type T is a hash table. +// The heuristic used is that the type contains an inner type `hasher` and does +// not contain an inner type `reverse_iterator`. +// If the container is iterable in reverse, then order might actually matter. +template +struct IsHashTable { + private: + template + static char test(typename U::hasher*, typename U::reverse_iterator*); + template + static int test(typename U::hasher*, ...); + template + static char test(...); + + public: + static const bool value = sizeof(test(nullptr, nullptr)) == sizeof(int); +}; + +template +const bool IsHashTable::value; + +template (0)) == sizeof(IsContainer)> +struct IsRecursiveContainerImpl; + +template +struct IsRecursiveContainerImpl : public std::false_type {}; + +// Since the IsRecursiveContainerImpl depends on the IsContainerTest we need to +// obey the same inconsistencies as the IsContainerTest, namely check if +// something is a container is relying on only const_iterator in C++11 and +// is relying on both const_iterator and iterator otherwise +template +struct IsRecursiveContainerImpl { + using value_type = decltype(*std::declval()); + using type = + std::is_same::type>::type, + C>; +}; + +// IsRecursiveContainer is a unary compile-time predicate that +// evaluates whether C is a recursive container type. A recursive container +// type is a container type whose value_type is equal to the container type +// itself. An example for a recursive container type is +// boost::filesystem::path, whose iterator has a value_type that is equal to +// boost::filesystem::path. +template +struct IsRecursiveContainer : public IsRecursiveContainerImpl::type {}; + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template +inline bool ArrayEq(const T& lhs, const U& rhs) { + return lhs == rhs; +} + +// This overload is used when k >= 1. +template +inline bool ArrayEq(const T (&lhs)[N], const U (&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template +inline void CopyArray(const T& from, U* to) { + *to = from; +} + +// This overload is used when k >= 1. +template +inline void CopyArray(const T (&from)[N], U (*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +// We use 2 different structs to allow non-copyable types to be used, as long +// as RelationToSourceReference() is passed. +struct RelationToSourceReference {}; +struct RelationToSourceCopy {}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. References the source. + NativeArray(const Element* array, size_t count, RelationToSourceReference) { + InitRef(array, count); + } + + // Constructs from a native array. Copies the source. + NativeArray(const Element* array, size_t count, RelationToSourceCopy) { + InitCopy(array, count); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + (this->*rhs.clone_)(rhs.array_, rhs.size_); + } + + ~NativeArray() { + if (clone_ != &NativeArray::InitRef) delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && ArrayEq(begin(), size(), rhs.begin()); + } + + private: + static_assert(!std::is_const::value, "Type must not be const"); + static_assert(!std::is_reference::value, + "Type must not be a reference"); + + // Initializes this object with a copy of the input. + void InitCopy(const Element* array, size_t a_size) { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + size_ = a_size; + clone_ = &NativeArray::InitCopy; + } + + // Initializes this object with a reference of the input. + void InitRef(const Element* array, size_t a_size) { + array_ = array; + size_ = a_size; + clone_ = &NativeArray::InitRef; + } + + const Element* array_; + size_t size_; + void (NativeArray::*clone_)(const Element*, size_t); +}; + +template +struct Ignore { + Ignore(...); // NOLINT +}; + +template +struct ElemFromListImpl; +template +struct ElemFromListImpl> { + // We make Ignore a template to solve a problem with MSVC. + // A non-template Ignore would work fine with `decltype(Ignore(I))...`, but + // MSVC doesn't understand how to deal with that pack expansion. + // Use `0 * I` to have a single instantiation of Ignore. + template + static R Apply(Ignore<0 * I>..., R (*)(), ...); +}; + +template +struct ElemFromList { + using type = decltype(ElemFromListImpl>::Apply( + static_cast(nullptr)...)); +}; + +struct FlatTupleConstructTag {}; + +template +class FlatTuple; + +template +struct FlatTupleElemBase; + +template +struct FlatTupleElemBase, I> { + using value_type = typename ElemFromList::type; + FlatTupleElemBase() = default; + template + explicit FlatTupleElemBase(FlatTupleConstructTag, Arg&& t) + : value(std::forward(t)) {} + value_type value; +}; + +template +struct FlatTupleBase; + +template +struct FlatTupleBase, std::index_sequence> + : FlatTupleElemBase, Idx>... { + using Indices = std::index_sequence; + FlatTupleBase() = default; + template + explicit FlatTupleBase(FlatTupleConstructTag, Args&&... args) + : FlatTupleElemBase, Idx>(FlatTupleConstructTag{}, + std::forward(args))... {} + + template + const typename ElemFromList::type& Get() const { + return FlatTupleElemBase, I>::value; + } + + template + typename ElemFromList::type& Get() { + return FlatTupleElemBase, I>::value; + } + + template + auto Apply(F&& f) -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } + + template + auto Apply(F&& f) const -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } +}; + +// Analog to std::tuple but with different tradeoffs. +// This class minimizes the template instantiation depth, thus allowing more +// elements than std::tuple would. std::tuple has been seen to require an +// instantiation depth of more than 10x the number of elements in some +// implementations. +// FlatTuple and ElemFromList are not recursive and have a fixed depth +// regardless of T... +// std::make_index_sequence, on the other hand, it is recursive but with an +// instantiation depth of O(ln(N)). +template +class FlatTuple + : private FlatTupleBase, + std::make_index_sequence> { + using Indices = + typename FlatTupleBase, + std::make_index_sequence>::Indices; + + public: + FlatTuple() = default; + template + explicit FlatTuple(FlatTupleConstructTag tag, Args&&... args) + : FlatTuple::FlatTupleBase(tag, std::forward(args)...) {} + + using FlatTuple::FlatTupleBase::Apply; + using FlatTuple::FlatTupleBase::Get; +}; + +// Utility functions to be called with static_assert to induce deprecation +// warnings. +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TEST_SUITE_P") +constexpr bool InstantiateTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE_P is deprecated, please use " + "TYPED_TEST_SUITE_P") +constexpr bool TypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE is deprecated, please use " + "TYPED_TEST_SUITE") +constexpr bool TypedTestCaseIsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "REGISTER_TYPED_TEST_CASE_P is deprecated, please use " + "REGISTER_TYPED_TEST_SUITE_P") +constexpr bool RegisterTypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TYPED_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TYPED_TEST_SUITE_P") +constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { return true; } + +} // namespace internal +} // namespace testing + +namespace std { +// Some standard library implementations use `struct tuple_size` and some use +// `class tuple_size`. Clang warns about the mismatch. +// https://reviews.llvm.org/D55466 +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +struct tuple_size> + : std::integral_constant {}; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} // namespace std + +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) = \ + ::testing::Message() + +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + +#define GTEST_FATAL_FAILURE_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) + +#define GTEST_NONFATAL_FAILURE_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) + +#define GTEST_SUCCESS_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) + +#define GTEST_SKIP_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip) + +// Suppress MSVC warning 4072 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +// NOTE: The "else" is important to keep this expansion to prevent a top-level +// "else" from attaching to our "if". +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } else /* NOLINT */ \ + static_assert(true, "") // User must have a semicolon after expansion. + +#if GTEST_HAS_EXCEPTIONS + +namespace testing { +namespace internal { + +class NeverThrown { + public: + const char* what() const noexcept { + return "this exception should never be thrown"; + } +}; + +} // namespace internal +} // namespace testing + +#if GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) ::testing::internal::GetTypeName(typeid(e)) + +#else // GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) \ + std::string { "an std::exception-derived error" } + +#endif // GTEST_HAS_RTTI + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (typename std::conditional< \ + std::is_same::type>::type, \ + std::exception>::value, \ + const ::testing::internal::NeverThrown&, const std::exception&>::type \ + e) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (...) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else /*NOLINT*/ \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \ + : fail(gtest_msg.value.c_str()) + +#if GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (std::exception const& e) { \ + gtest_msg.value = "it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (...) { \ + gtest_msg.value = "it throws."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__) \ + : fail(("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: " + \ + gtest_msg.value) \ + .c_str()) + +#define GTEST_TEST_ANY_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + bool gtest_caught_any = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (...) { \ + gtest_caught_any = true; \ + } \ + if (!gtest_caught_any) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__) \ + : fail("Expected: " #statement \ + " throws an exception.\n" \ + " Actual: it doesn't.") + +// Implements Boolean test assertions such as EXPECT_TRUE. expression can be +// either a boolean expression or an AssertionResult. text is a textual +// representation of expression as it was passed into the EXPECT_TRUE. +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + ; \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage( \ + gtest_ar_, text, #actual, #expected) \ + .c_str()) + +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + const ::testing::internal::HasNewFatalFailureHelper \ + gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else /* NOLINT */ \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__) \ + : fail("Expected: " #statement \ + " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") + +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + test_suite_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \ + static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \ + "test_suite_name must not be empty"); \ + static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \ + "test_name must not be empty"); \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public parent_class { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \ + ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + (const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ + const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name) &) = delete; /* NOLINT */ \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + (GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) noexcept = delete; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ + GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name) &&) noexcept = delete; /* NOLINT */ \ + \ + private: \ + void TestBody() override; \ + GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static ::testing::TestInfo* const \ + test_info_; \ + }; \ + \ + ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::test_info_ = \ + ::testing::internal::MakeAndRegisterTestInfo( \ + #test_suite_name, #test_name, nullptr, nullptr, \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \ + new ::testing::internal::TestFactoryImpl); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/gtest-param-util.h b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-param-util.h new file mode 100644 index 0000000..cc7ea53 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-param-util.h @@ -0,0 +1,1030 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Type and function utilities for implementing parameterized tests. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-printers.h" +#include "gtest/gtest-test-part.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { +// Input to a parameterized test name generator, describing a test parameter. +// Consists of the parameter value and the integer parameter index. +template +struct TestParamInfo { + TestParamInfo(const ParamType& a_param, size_t an_index) + : param(a_param), index(an_index) {} + ParamType param; + size_t index; +}; + +// A builtin parameterized test name generator which returns the result of +// testing::PrintToString. +struct PrintToStringParamName { + template + std::string operator()(const TestParamInfo& info) const { + return PrintToString(info.param); + } +}; + +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// Utility Functions + +// Outputs a message explaining invalid registration of different +// fixture class for the same test suite. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name, + const CodeLocation& code_location); + +template +class ParamGeneratorInterface; +template +class ParamGenerator; + +// Interface for iterating over elements provided by an implementation +// of ParamGeneratorInterface. +template +class ParamIteratorInterface { + public: + virtual ~ParamIteratorInterface() = default; + // A pointer to the base generator instance. + // Used only for the purposes of iterator comparison + // to make sure that two iterators belong to the same generator. + virtual const ParamGeneratorInterface* BaseGenerator() const = 0; + // Advances iterator to point to the next element + // provided by the generator. The caller is responsible + // for not calling Advance() on an iterator equal to + // BaseGenerator()->End(). + virtual void Advance() = 0; + // Clones the iterator object. Used for implementing copy semantics + // of ParamIterator. + virtual ParamIteratorInterface* Clone() const = 0; + // Dereferences the current iterator and provides (read-only) access + // to the pointed value. It is the caller's responsibility not to call + // Current() on an iterator equal to BaseGenerator()->End(). + // Used for implementing ParamGenerator::operator*(). + virtual const T* Current() const = 0; + // Determines whether the given iterator and other point to the same + // element in the sequence generated by the generator. + // Used for implementing ParamGenerator::operator==(). + virtual bool Equals(const ParamIteratorInterface& other) const = 0; +}; + +// Class iterating over elements provided by an implementation of +// ParamGeneratorInterface. It wraps ParamIteratorInterface +// and implements the const forward iterator concept. +template +class ParamIterator { + public: + typedef T value_type; + typedef const T& reference; + typedef ptrdiff_t difference_type; + + // ParamIterator assumes ownership of the impl_ pointer. + ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} + ParamIterator& operator=(const ParamIterator& other) { + if (this != &other) impl_.reset(other.impl_->Clone()); + return *this; + } + + const T& operator*() const { return *impl_->Current(); } + const T* operator->() const { return impl_->Current(); } + // Prefix version of operator++. + ParamIterator& operator++() { + impl_->Advance(); + return *this; + } + // Postfix version of operator++. + ParamIterator operator++(int /*unused*/) { + ParamIteratorInterface* clone = impl_->Clone(); + impl_->Advance(); + return ParamIterator(clone); + } + bool operator==(const ParamIterator& other) const { + return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); + } + bool operator!=(const ParamIterator& other) const { + return !(*this == other); + } + + private: + friend class ParamGenerator; + explicit ParamIterator(ParamIteratorInterface* impl) : impl_(impl) {} + std::unique_ptr> impl_; +}; + +// ParamGeneratorInterface is the binary interface to access generators +// defined in other translation units. +template +class ParamGeneratorInterface { + public: + typedef T ParamType; + + virtual ~ParamGeneratorInterface() = default; + + // Generator interface definition + virtual ParamIteratorInterface* Begin() const = 0; + virtual ParamIteratorInterface* End() const = 0; +}; + +// Wraps ParamGeneratorInterface and provides general generator syntax +// compatible with the STL Container concept. +// This class implements copy initialization semantics and the contained +// ParamGeneratorInterface instance is shared among all copies +// of the original object. This is possible because that instance is immutable. +template +class ParamGenerator { + public: + typedef ParamIterator iterator; + + explicit ParamGenerator(ParamGeneratorInterface* impl) : impl_(impl) {} + ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} + + ParamGenerator& operator=(const ParamGenerator& other) { + impl_ = other.impl_; + return *this; + } + + iterator begin() const { return iterator(impl_->Begin()); } + iterator end() const { return iterator(impl_->End()); } + + private: + std::shared_ptr> impl_; +}; + +// Generates values from a range of two comparable values. Can be used to +// generate sequences of user-defined types that implement operator+() and +// operator<(). +// This class is used in the Range() function. +template +class RangeGenerator : public ParamGeneratorInterface { + public: + RangeGenerator(T begin, T end, IncrementT step) + : begin_(begin), + end_(end), + step_(step), + end_index_(CalculateEndIndex(begin, end, step)) {} + ~RangeGenerator() override = default; + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, begin_, 0, step_); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, end_, end_index_, step_); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, T value, int index, + IncrementT step) + : base_(base), value_(value), index_(index), step_(step) {} + ~Iterator() override = default; + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + value_ = static_cast(value_ + step_); + index_++; + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + const T* Current() const override { return &value_; } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const int other_index = + CheckedDowncastToActualType(&other)->index_; + return index_ == other_index; + } + + private: + Iterator(const Iterator& other) + : ParamIteratorInterface(), + base_(other.base_), + value_(other.value_), + index_(other.index_), + step_(other.step_) {} + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + T value_; + int index_; + const IncrementT step_; + }; // class RangeGenerator::Iterator + + static int CalculateEndIndex(const T& begin, const T& end, + const IncrementT& step) { + int end_index = 0; + for (T i = begin; i < end; i = static_cast(i + step)) end_index++; + return end_index; + } + + // No implementation - assignment is unsupported. + void operator=(const RangeGenerator& other); + + const T begin_; + const T end_; + const IncrementT step_; + // The index for the end() iterator. All the elements in the generated + // sequence are indexed (0-based) to aid iterator comparison. + const int end_index_; +}; // class RangeGenerator + +// Generates values from a pair of STL-style iterators. Used in the +// ValuesIn() function. The elements are copied from the source range +// since the source can be located on the stack, and the generator +// is likely to persist beyond that stack frame. +template +class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface { + public: + template + ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) + : container_(begin, end) {} + ~ValuesInIteratorRangeGenerator() override = default; + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, container_.begin()); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, container_.end()); + } + + private: + typedef typename ::std::vector ContainerType; + + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + typename ContainerType::const_iterator iterator) + : base_(base), iterator_(iterator) {} + ~Iterator() override = default; + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + ++iterator_; + value_.reset(); + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + // We need to use cached value referenced by iterator_ because *iterator_ + // can return a temporary object (and of type other then T), so just + // having "return &*iterator_;" doesn't work. + // value_ is updated here and not in Advance() because Advance() + // can advance iterator_ beyond the end of the range, and we cannot + // detect that fact. The client code, on the other hand, is + // responsible for not calling Current() on an out-of-range iterator. + const T* Current() const override { + if (value_.get() == nullptr) value_.reset(new T(*iterator_)); + return value_.get(); + } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + return iterator_ == + CheckedDowncastToActualType(&other)->iterator_; + } + + private: + Iterator(const Iterator& other) + // The explicit constructor call suppresses a false warning + // emitted by gcc when supplied with the -Wextra option. + : ParamIteratorInterface(), + base_(other.base_), + iterator_(other.iterator_) {} + + const ParamGeneratorInterface* const base_; + typename ContainerType::const_iterator iterator_; + // A cached value of *iterator_. We keep it here to allow access by + // pointer in the wrapping iterator's operator->(). + // value_ needs to be mutable to be accessed in Current(). + // Use of std::unique_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable std::unique_ptr value_; + }; // class ValuesInIteratorRangeGenerator::Iterator + + // No implementation - assignment is unsupported. + void operator=(const ValuesInIteratorRangeGenerator& other); + + const ContainerType container_; +}; // class ValuesInIteratorRangeGenerator + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Default parameterized test name generator, returns a string containing the +// integer test parameter index. +template +std::string DefaultParamName(const TestParamInfo& info) { + return std::to_string(info.index); +} + +template +void TestNotEmpty() { + static_assert(sizeof(T) == 0, "Empty arguments are not allowed."); +} +template +void TestNotEmpty(const T&) {} + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Stores a parameter value and later creates tests parameterized with that +// value. +template +class ParameterizedTestFactory : public TestFactoryBase { + public: + typedef typename TestClass::ParamType ParamType; + explicit ParameterizedTestFactory(ParamType parameter) + : parameter_(parameter) {} + Test* CreateTest() override { + TestClass::SetParam(¶meter_); + return new TestClass(); + } + + private: + const ParamType parameter_; + + ParameterizedTestFactory(const ParameterizedTestFactory&) = delete; + ParameterizedTestFactory& operator=(const ParameterizedTestFactory&) = delete; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactoryBase is a base class for meta-factories that create +// test factories for passing into MakeAndRegisterTestInfo function. +template +class TestMetaFactoryBase { + public: + virtual ~TestMetaFactoryBase() = default; + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactory creates test factories for passing into +// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives +// ownership of test factory pointer, same factory object cannot be passed +// into that method twice. But ParameterizedTestSuiteInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template +class TestMetaFactory + : public TestMetaFactoryBase { + public: + using ParamType = typename TestSuite::ParamType; + + TestMetaFactory() = default; + + TestFactoryBase* CreateTestFactory(ParamType parameter) override { + return new ParameterizedTestFactory(parameter); + } + + private: + TestMetaFactory(const TestMetaFactory&) = delete; + TestMetaFactory& operator=(const TestMetaFactory&) = delete; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfoBase is a generic interface +// to ParameterizedTestSuiteInfo classes. ParameterizedTestSuiteInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_SUITE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestSuiteRegistry class holds +// a collection of pointers to the ParameterizedTestSuiteInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestSuiteInfoBase { + public: + virtual ~ParameterizedTestSuiteInfoBase() = default; + + // Base part of test suite name for display purposes. + virtual const std::string& GetTestSuiteName() const = 0; + // Test suite id to verify identity. + virtual TypeId GetTestSuiteTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test suite right before running them in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestSuiteInfoBase() {} + + private: + ParameterizedTestSuiteInfoBase(const ParameterizedTestSuiteInfoBase&) = + delete; + ParameterizedTestSuiteInfoBase& operator=( + const ParameterizedTestSuiteInfoBase&) = delete; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Report a the name of a test_suit as safe to ignore +// as the side effect of construction of this type. +struct GTEST_API_ MarkAsIgnored { + explicit MarkAsIgnored(const char* test_suite); +}; + +GTEST_API_ void InsertSyntheticTestCase(const std::string& name, + CodeLocation location, bool has_test_p); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test suite and generators +// obtained from INSTANTIATE_TEST_SUITE_P macro invocations for that +// test suite. It registers tests with all values generated by all +// generators when asked. +template +class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestSuiteInstantiation(). + using ParamType = typename TestSuite::ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator(GeneratorCreationFunc)(); + using ParamNameGeneratorFunc = std::string(const TestParamInfo&); + + explicit ParameterizedTestSuiteInfo(std::string name, + CodeLocation code_location) + : test_suite_name_(std::move(name)), + code_location_(std::move(code_location)) {} + + // Test suite base name for display purposes. + const std::string& GetTestSuiteName() const override { + return test_suite_name_; + } + // Test suite id to verify identity. + TypeId GetTestSuiteTypeId() const override { return GetTypeId(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_suite_name is the base name of the test suite (without invocation + // prefix). test_base_name is the name of an individual test without + // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is + // test suite base name and DoBar is test base name. + void AddTestPattern(const char*, + const char* test_base_name, + TestMetaFactoryBase* meta_factory, + CodeLocation code_location) { + tests_.emplace_back( + new TestInfo(test_base_name, meta_factory, std::move(code_location))); + } + // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestSuiteInstantiation(std::string instantiation_name, + GeneratorCreationFunc* func, + ParamNameGeneratorFunc* name_func, + const char* file, int line) { + instantiations_.emplace_back(std::move(instantiation_name), func, name_func, + file, line); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test suite + // right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more than once. + void RegisterTests() override { + bool generated_instantiations = false; + + std::string test_suite_name; + std::string test_name; + for (const std::shared_ptr& test_info : tests_) { + for (const InstantiationInfo& instantiation : instantiations_) { + const std::string& instantiation_name = instantiation.name; + ParamGenerator generator((*instantiation.generator)()); + ParamNameGeneratorFunc* name_func = instantiation.name_func; + const char* file = instantiation.file; + int line = instantiation.line; + + if (!instantiation_name.empty()) + test_suite_name = instantiation_name + "/"; + else + test_suite_name.clear(); + test_suite_name += test_suite_name_; + + size_t i = 0; + std::set test_param_names; + for (const auto& param : generator) { + generated_instantiations = true; + + test_name.clear(); + + std::string param_name = + name_func(TestParamInfo(param, i)); + + GTEST_CHECK_(IsValidParamName(param_name)) + << "Parameterized test name '" << param_name + << "' is invalid (contains spaces, dashes, or any " + "non-alphanumeric characters other than underscores), in " + << file << " line " << line << "" << std::endl; + + GTEST_CHECK_(test_param_names.count(param_name) == 0) + << "Duplicate parameterized test name '" << param_name << "', in " + << file << " line " << line << std::endl; + + if (!test_info->test_base_name.empty()) { + test_name.append(test_info->test_base_name).append("/"); + } + test_name += param_name; + + test_param_names.insert(std::move(param_name)); + + MakeAndRegisterTestInfo( + test_suite_name, test_name.c_str(), + nullptr, // No type parameter. + PrintToString(param).c_str(), test_info->code_location, + GetTestSuiteTypeId(), + SuiteApiResolver::GetSetUpCaseOrSuite(file, line), + SuiteApiResolver::GetTearDownCaseOrSuite(file, line), + test_info->test_meta_factory->CreateTestFactory(param)); + ++i; + } // for param + } // for instantiation + } // for test_info + + if (!generated_instantiations) { + // There are no generaotrs, or they all generate nothing ... + InsertSyntheticTestCase(GetTestSuiteName(), code_location_, + !tests_.empty()); + } + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_base_name, + TestMetaFactoryBase* a_test_meta_factory, + CodeLocation a_code_location) + : test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory), + code_location(std::move(a_code_location)) {} + + const std::string test_base_name; + const std::unique_ptr> test_meta_factory; + const CodeLocation code_location; + }; + using TestInfoContainer = ::std::vector>; + // Records data received from INSTANTIATE_TEST_SUITE_P macros: + // + struct InstantiationInfo { + InstantiationInfo(std::string name_in, GeneratorCreationFunc* generator_in, + ParamNameGeneratorFunc* name_func_in, const char* file_in, + int line_in) + : name(std::move(name_in)), + generator(generator_in), + name_func(name_func_in), + file(file_in), + line(line_in) {} + + std::string name; + GeneratorCreationFunc* generator; + ParamNameGeneratorFunc* name_func; + const char* file; + int line; + }; + typedef ::std::vector InstantiationContainer; + + static bool IsValidParamName(const std::string& name) { + // Check for empty string + if (name.empty()) return false; + + // Check for invalid characters + for (std::string::size_type index = 0; index < name.size(); ++index) { + if (!IsAlNum(name[index]) && name[index] != '_') return false; + } + + return true; + } + + const std::string test_suite_name_; + CodeLocation code_location_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + ParameterizedTestSuiteInfo(const ParameterizedTestSuiteInfo&) = delete; + ParameterizedTestSuiteInfo& operator=(const ParameterizedTestSuiteInfo&) = + delete; +}; // class ParameterizedTestSuiteInfo + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +template +using ParameterizedTestCaseInfo = ParameterizedTestSuiteInfo; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteRegistry contains a map of +// ParameterizedTestSuiteInfoBase classes accessed by test suite names. TEST_P +// and INSTANTIATE_TEST_SUITE_P macros use it to locate their corresponding +// ParameterizedTestSuiteInfo descriptors. +class ParameterizedTestSuiteRegistry { + public: + ParameterizedTestSuiteRegistry() = default; + ~ParameterizedTestSuiteRegistry() { + for (auto& test_suite_info : test_suite_infos_) { + delete test_suite_info; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test suite. + template + ParameterizedTestSuiteInfo* GetTestSuitePatternHolder( + std::string test_suite_name, CodeLocation code_location) { + ParameterizedTestSuiteInfo* typed_test_info = nullptr; + + auto item_it = suite_name_to_info_index_.find(test_suite_name); + if (item_it != suite_name_to_info_index_.end()) { + auto* test_suite_info = test_suite_infos_[item_it->second]; + if (test_suite_info->GetTestSuiteTypeId() != GetTypeId()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test suite setup and tear-down in this case. + ReportInvalidTestSuiteType(test_suite_name.c_str(), code_location); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = + CheckedDowncastToActualType>( + test_suite_info); + } + } + if (typed_test_info == nullptr) { + typed_test_info = new ParameterizedTestSuiteInfo( + test_suite_name, std::move(code_location)); + suite_name_to_info_index_.emplace(std::move(test_suite_name), + test_suite_infos_.size()); + test_suite_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (auto& test_suite_info : test_suite_infos_) { + test_suite_info->RegisterTests(); + } + } +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + template + ParameterizedTestCaseInfo* GetTestCasePatternHolder( + std::string test_case_name, CodeLocation code_location) { + return GetTestSuitePatternHolder(std::move(test_case_name), + std::move(code_location)); + } + +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + private: + using TestSuiteInfoContainer = ::std::vector; + + TestSuiteInfoContainer test_suite_infos_; + ::std::unordered_map suite_name_to_info_index_; + + ParameterizedTestSuiteRegistry(const ParameterizedTestSuiteRegistry&) = + delete; + ParameterizedTestSuiteRegistry& operator=( + const ParameterizedTestSuiteRegistry&) = delete; +}; + +// Keep track of what type-parameterized test suite are defined and +// where as well as which are intatiated. This allows susequently +// identifying suits that are defined but never used. +class TypeParameterizedTestSuiteRegistry { + public: + // Add a suite definition + void RegisterTestSuite(const char* test_suite_name, + CodeLocation code_location); + + // Add an instantiation of a suit. + void RegisterInstantiation(const char* test_suite_name); + + // For each suit repored as defined but not reported as instantiation, + // emit a test that reports that fact (configurably, as an error). + void CheckForInstantiations(); + + private: + struct TypeParameterizedTestSuiteInfo { + explicit TypeParameterizedTestSuiteInfo(CodeLocation c) + : code_location(std::move(c)), instantiated(false) {} + + CodeLocation code_location; + bool instantiated; + }; + + std::map suites_; +}; + +} // namespace internal + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { +// Used in the Values() function to provide polymorphic capabilities. + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4100) + +template +class ValueArray { + public: + explicit ValueArray(Ts... v) : v_(FlatTupleConstructTag{}, std::move(v)...) {} + + template + operator ParamGenerator() const { // NOLINT + return ValuesIn(MakeVector(std::make_index_sequence())); + } + + private: + template + std::vector MakeVector(std::index_sequence) const { + return std::vector{static_cast(v_.template Get())...}; + } + + FlatTuple v_; +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4100 + +template +class CartesianProductGenerator + : public ParamGeneratorInterface<::std::tuple> { + public: + typedef ::std::tuple ParamType; + + CartesianProductGenerator(const std::tuple...>& g) + : generators_(g) {} + ~CartesianProductGenerator() override = default; + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, generators_, false); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, generators_, true); + } + + private: + template + class IteratorImpl; + template + class IteratorImpl> + : public ParamIteratorInterface { + public: + IteratorImpl(const ParamGeneratorInterface* base, + const std::tuple...>& generators, + bool is_end) + : base_(base), + begin_(std::get(generators).begin()...), + end_(std::get(generators).end()...), + current_(is_end ? end_ : begin_) { + ComputeCurrentValue(); + } + ~IteratorImpl() override = default; + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + void Advance() override { + assert(!AtEnd()); + // Advance the last iterator. + ++std::get(current_); + // if that reaches end, propagate that up. + AdvanceIfEnd(); + ComputeCurrentValue(); + } + ParamIteratorInterface* Clone() const override { + return new IteratorImpl(*this); + } + + const ParamType* Current() const override { return current_value_.get(); } + + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const IteratorImpl* typed_other = + CheckedDowncastToActualType(&other); + + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + if (AtEnd() && typed_other->AtEnd()) return true; + + bool same = true; + bool dummy[] = { + (same = same && std::get(current_) == + std::get(typed_other->current_))...}; + (void)dummy; + return same; + } + + private: + template + void AdvanceIfEnd() { + if (std::get(current_) != std::get(end_)) return; + + bool last = ThisI == 0; + if (last) { + // We are done. Nothing else to propagate. + return; + } + + constexpr size_t NextI = ThisI - (ThisI != 0); + std::get(current_) = std::get(begin_); + ++std::get(current_); + AdvanceIfEnd(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = std::make_shared(*std::get(current_)...); + } + bool AtEnd() const { + bool at_end = false; + bool dummy[] = { + (at_end = at_end || std::get(current_) == std::get(end_))...}; + (void)dummy; + return at_end; + } + + const ParamGeneratorInterface* const base_; + std::tuple::iterator...> begin_; + std::tuple::iterator...> end_; + std::tuple::iterator...> current_; + std::shared_ptr current_value_; + }; + + using Iterator = IteratorImpl>; + + std::tuple...> generators_; +}; + +template +class CartesianProductHolder { + public: + CartesianProductHolder(const Gen&... g) : generators_(g...) {} + template + operator ParamGenerator<::std::tuple>() const { + return ParamGenerator<::std::tuple>( + new CartesianProductGenerator(generators_)); + } + + private: + std::tuple generators_; +}; + +template +class ParamGeneratorConverter : public ParamGeneratorInterface { + public: + ParamGeneratorConverter(ParamGenerator gen) // NOLINT + : generator_(std::move(gen)) {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, generator_.begin(), generator_.end()); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, generator_.end(), generator_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, ParamIterator it, + ParamIterator end) + : base_(base), it_(it), end_(end) { + if (it_ != end_) value_ = std::make_shared(static_cast(*it_)); + } + ~Iterator() override = default; + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + ++it_; + if (it_ != end_) value_ = std::make_shared(static_cast(*it_)); + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + const To* Current() const override { return value_.get(); } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const ParamIterator other_it = + CheckedDowncastToActualType(&other)->it_; + return it_ == other_it; + } + + private: + Iterator(const Iterator& other) = default; + + const ParamGeneratorInterface* const base_; + ParamIterator it_; + ParamIterator end_; + std::shared_ptr value_; + }; // class ParamGeneratorConverter::Iterator + + ParamGenerator generator_; +}; // class ParamGeneratorConverter + +template +class ParamConverterGenerator { + public: + ParamConverterGenerator(ParamGenerator g) // NOLINT + : generator_(std::move(g)) {} + + template + operator ParamGenerator() const { // NOLINT + return ParamGenerator(new ParamGeneratorConverter(generator_)); + } + + private: + ParamGenerator generator_; +}; + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/gtest-port-arch.h b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-port-arch.h new file mode 100644 index 0000000..7ec968f --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-port-arch.h @@ -0,0 +1,124 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the GTEST_OS_* macro. +// It is separate from gtest-port.h so that custom/gtest-port.h can include it. + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +#define GTEST_OS_CYGWIN 1 +#elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__) +#define GTEST_OS_WINDOWS_MINGW 1 +#define GTEST_OS_WINDOWS 1 +#elif defined _WIN32 +#define GTEST_OS_WINDOWS 1 +#ifdef _WIN32_WCE +#define GTEST_OS_WINDOWS_MOBILE 1 +#elif defined(WINAPI_FAMILY) +#include +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define GTEST_OS_WINDOWS_DESKTOP 1 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +#define GTEST_OS_WINDOWS_PHONE 1 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +#define GTEST_OS_WINDOWS_RT 1 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE) +#define GTEST_OS_WINDOWS_PHONE 1 +#define GTEST_OS_WINDOWS_TV_TITLE 1 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_GAMES) +#define GTEST_OS_WINDOWS_GAMES 1 +#else +// WINAPI_FAMILY defined but no known partition matched. +// Default to desktop. +#define GTEST_OS_WINDOWS_DESKTOP 1 +#endif +#else +#define GTEST_OS_WINDOWS_DESKTOP 1 +#endif // _WIN32_WCE +#elif defined __OS2__ +#define GTEST_OS_OS2 1 +#elif defined __APPLE__ +#define GTEST_OS_MAC 1 +#include +#if TARGET_OS_IPHONE +#define GTEST_OS_IOS 1 +#endif +#elif defined __DragonFly__ +#define GTEST_OS_DRAGONFLY 1 +#elif defined __FreeBSD__ +#define GTEST_OS_FREEBSD 1 +#elif defined __Fuchsia__ +#define GTEST_OS_FUCHSIA 1 +#elif defined(__GNU__) +#define GTEST_OS_GNU_HURD 1 +#elif defined(__GLIBC__) && defined(__FreeBSD_kernel__) +#define GTEST_OS_GNU_KFREEBSD 1 +#elif defined __linux__ +#define GTEST_OS_LINUX 1 +#if defined __ANDROID__ +#define GTEST_OS_LINUX_ANDROID 1 +#endif +#elif defined __MVS__ +#define GTEST_OS_ZOS 1 +#elif defined(__sun) && defined(__SVR4) +#define GTEST_OS_SOLARIS 1 +#elif defined(_AIX) +#define GTEST_OS_AIX 1 +#elif defined(__hpux) +#define GTEST_OS_HPUX 1 +#elif defined __native_client__ +#define GTEST_OS_NACL 1 +#elif defined __NetBSD__ +#define GTEST_OS_NETBSD 1 +#elif defined __OpenBSD__ +#define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +#define GTEST_OS_QNX 1 +#elif defined(__HAIKU__) +#define GTEST_OS_HAIKU 1 +#elif defined ESP8266 +#define GTEST_OS_ESP8266 1 +#elif defined ESP32 +#define GTEST_OS_ESP32 1 +#elif defined(__XTENSA__) +#define GTEST_OS_XTENSA 1 +#elif defined(__hexagon__) +#define GTEST_OS_QURT 1 +#elif defined(CPU_QN9090) || defined(CPU_QN9090HN) +#define GTEST_OS_NXP_QN9090 1 +#elif defined(NRF52) +#define GTEST_OS_NRF52 1 +#endif // __CYGWIN__ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/gtest-port.h b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-port.h new file mode 100644 index 0000000..ca18513 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-port.h @@ -0,0 +1,2544 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Low-level types and utilities for porting Google Test to various +// platforms. All macros ending with _ and symbols defined in an +// internal namespace are subject to change without notice. Code +// outside Google Test MUST NOT USE THEM DIRECTLY. Macros that don't +// end with _ are part of Google Test's public API and can be used by +// code outside Google Test. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// Environment-describing macros +// ----------------------------- +// +// Google Test can be used in many different environments. Macros in +// this section tell Google Test what kind of environment it is being +// used in, such that Google Test can provide environment-specific +// features and implementations. +// +// Google Test tries to automatically detect the properties of its +// environment, so users usually don't need to worry about these +// macros. However, the automatic detection is not perfect. +// Sometimes it's necessary for a user to define some of the following +// macros in the build script to override Google Test's decisions. +// +// If the user doesn't define a macro in the list, Google Test will +// provide a default definition. After this header is #included, all +// macros in this list will be defined to either 1 or 0. +// +// Notes to maintainers: +// - Each macro here is a user-tweakable knob; do not grow the list +// lightly. +// - Use #if to key off these macros. Don't use #ifdef or "#if +// defined(...)", which will not work as these macros are ALWAYS +// defined. +// +// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) +// is/isn't available. +// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions +// are enabled. +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. +// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that +// is/isn't available. +// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't +// enabled. +// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that +// std::wstring does/doesn't work (Google Test can +// be used where std::wstring is unavailable). +// GTEST_HAS_FILE_SYSTEM - Define it to 1/0 to indicate whether or not a +// file system is/isn't available. +// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the +// compiler supports Microsoft's "Structured +// Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). +// GTEST_LINKED_AS_SHARED_LIBRARY +// - Define to 1 when compiling tests that use +// Google Test as a shared library (known as +// DLL on Windows). +// GTEST_CREATE_SHARED_LIBRARY +// - Define to 1 when compiling Google Test itself +// as a shared library. +// GTEST_DEFAULT_DEATH_TEST_STYLE +// - The default value of --gtest_death_test_style. +// The legacy default has been "fast" in the open +// source version since 2008. The recommended value +// is "threadsafe", and can be set in +// custom/gtest-port.h. + +// Platform-indicating macros +// -------------------------- +// +// Macros indicating the platform on which Google Test is being used +// (a macro is defined to 1 if compiled on the given platform; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_DRAGONFLY - DragonFlyBSD +// GTEST_OS_FREEBSD - FreeBSD +// GTEST_OS_FUCHSIA - Fuchsia +// GTEST_OS_GNU_HURD - GNU/Hurd +// GTEST_OS_GNU_KFREEBSD - GNU/kFreeBSD +// GTEST_OS_HAIKU - Haiku +// GTEST_OS_HPUX - HP-UX +// GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android +// GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_NETBSD - NetBSD +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_OS2 - OS/2 +// GTEST_OS_QNX - QNX +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) +// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop +// GTEST_OS_WINDOWS_MINGW - MinGW +// GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_WINDOWS_PHONE - Windows Phone +// GTEST_OS_WINDOWS_RT - Windows Store App/WinRT +// GTEST_OS_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Mac OS X, and Windows have the +// most stable support. Since core members of the Google Test project +// don't have access to other platforms, support for them may be less +// stable. If you notice any problems on your platform, please notify +// googletestframework@googlegroups.com (patches for fixing them are +// even more welcome!). +// +// It is possible that none of the GTEST_OS_* macros are defined. + +// Feature-indicating macros +// ------------------------- +// +// Macros indicating which Google Test features are available (a macro +// is defined to 1 if the corresponding feature is supported; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// These macros are public so that portable tests can be written. +// Such tests typically surround code using a feature with an #ifdef +// which controls that code. For example: +// +// #ifdef GTEST_HAS_DEATH_TEST +// EXPECT_DEATH(DoSomethingDeadly()); +// #endif +// +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_IS_THREADSAFE - Google Test is thread-safe. +// GTEST_USES_RE2 - the RE2 regular expression library is used +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. +// GTEST_USES_SIMPLE_RE - our own simple regex is used; +// the above RE\b(s) are mutually exclusive. +// GTEST_HAS_ABSL - Google Test is compiled with Abseil. + +// Misc public macros +// ------------------ +// +// GTEST_FLAG(flag_name) - references the variable corresponding to +// the given Google Test flag. + +// Internal utilities +// ------------------ +// +// The following macros and utilities are for Google Test's INTERNAL +// use only. Code outside Google Test MUST NOT USE THEM DIRECTLY. +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is +// suppressed (constant conditional). +// GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127 +// is suppressed. +// GTEST_INTERNAL_HAS_ANY - for enabling UniversalPrinter or +// UniversalPrinter specializations. +// Always defined to 0 or 1. +// GTEST_INTERNAL_HAS_OPTIONAL - for enabling UniversalPrinter +// or +// UniversalPrinter +// specializations. Always defined to 0 or 1. +// GTEST_INTERNAL_HAS_STD_SPAN - for enabling UniversalPrinter +// specializations. Always defined to 0 or 1 +// GTEST_INTERNAL_HAS_STRING_VIEW - for enabling Matcher or +// Matcher +// specializations. Always defined to 0 or 1. +// GTEST_INTERNAL_HAS_VARIANT - for enabling UniversalPrinter or +// UniversalPrinter +// specializations. Always defined to 0 or 1. +// GTEST_USE_OWN_FLAGFILE_FLAG_ - Always defined to 0 or 1. +// GTEST_HAS_CXXABI_H_ - Always defined to 0 or 1. +// GTEST_CAN_STREAM_RESULTS_ - Always defined to 0 or 1. +// GTEST_HAS_ALT_PATH_SEP_ - Always defined to 0 or 1. +// GTEST_WIDE_STRING_USES_UTF16_ - Always defined to 0 or 1. +// GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ - Always defined to 0 or 1. +// GTEST_HAS_NOTIFICATION_- Always defined to 0 or 1. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// +// Regular expressions: +// RE - a simple regular expression class using +// 1) the RE2 syntax on all platforms when built with RE2 +// and Abseil as dependencies +// 2) the POSIX Extended Regular Expression syntax on +// UNIX-like platforms, +// 3) A reduced regular exception syntax on other platforms, +// including Windows. +// Logging: +// GTEST_LOG_() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stdout and stderr capturing: +// CaptureStdout() - starts capturing stdout. +// GetCapturedStdout() - stops capturing stdout and returns the captured +// string. +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// TimeInMillis - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GetInjectableArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an int32_t environment variable. +// StringFromGTestEnv() - parses a string environment variable. +// +// Deprecation warnings: +// GTEST_INTERNAL_DEPRECATED(message) - attribute marking a function as +// deprecated; calling a marked function +// should generate a compiler warning + +// The definition of GTEST_INTERNAL_CPLUSPLUS_LANG comes first because it can +// potentially be used as an #include guard. +#if defined(_MSVC_LANG) +#define GTEST_INTERNAL_CPLUSPLUS_LANG _MSVC_LANG +#elif defined(__cplusplus) +#define GTEST_INTERNAL_CPLUSPLUS_LANG __cplusplus +#endif + +#if !defined(GTEST_INTERNAL_CPLUSPLUS_LANG) || \ + GTEST_INTERNAL_CPLUSPLUS_LANG < 201402L +#error C++ versions less than C++14 are not supported. +#endif + +// MSVC >= 19.11 (VS 2017 Update 3) supports __has_include. +#ifdef __has_include +#define GTEST_INTERNAL_HAS_INCLUDE __has_include +#else +#define GTEST_INTERNAL_HAS_INCLUDE(...) 0 +#endif + +// Detect C++ feature test macros as gracefully as possible. +// MSVC >= 19.15, Clang >= 3.4.1, and GCC >= 4.1.2 support feature test macros. +#if GTEST_INTERNAL_CPLUSPLUS_LANG >= 202002L && \ + (!defined(__has_include) || GTEST_INTERNAL_HAS_INCLUDE()) +#include // C++20 and later +#elif (!defined(__has_include) || GTEST_INTERNAL_HAS_INCLUDE()) +#include // Pre-C++20 +#endif + +#include // for isspace, etc +#include // for ptrdiff_t +#include +#include +#include + +#include +// #include // Guarded by GTEST_IS_THREADSAFE below +#include +#include +#include +#include +#include +#include +#include +// #include // Guarded by GTEST_IS_THREADSAFE below +#include +#include +#include + +#ifndef _WIN32_WCE +#include +#include +#endif // !_WIN32_WCE + +#if defined __APPLE__ +#include +#include +#endif + +#include "gtest/internal/custom/gtest-port.h" +#include "gtest/internal/gtest-port-arch.h" + +#ifndef GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ +#define GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ 0 +#endif + +#ifndef GTEST_HAS_NOTIFICATION_ +#define GTEST_HAS_NOTIFICATION_ 0 +#endif + +#if defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS) +#define GTEST_INTERNAL_HAS_ABSL_FLAGS // Used only in this file. +#include "absl/flags/declare.h" +#include "absl/flags/flag.h" +#include "absl/flags/reflection.h" +#endif + +#if !defined(GTEST_DEV_EMAIL_) +#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" +#define GTEST_FLAG_PREFIX_ "gtest_" +#define GTEST_FLAG_PREFIX_DASH_ "gtest-" +#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" +#define GTEST_NAME_ "Google Test" +#define GTEST_PROJECT_URL_ "https://github.com/google/googletest/" +#endif // !defined(GTEST_DEV_EMAIL_) + +#if !defined(GTEST_INIT_GOOGLE_TEST_NAME_) +#define GTEST_INIT_GOOGLE_TEST_NAME_ "testing::InitGoogleTest" +#endif // !defined(GTEST_INIT_GOOGLE_TEST_NAME_) + +// Determines the version of gcc that is used to compile this. +#ifdef __GNUC__ +// 40302 means version 4.3.2. +#define GTEST_GCC_VER_ \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +// Macros for disabling Microsoft Visual C++ warnings. +// +// GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385) +// /* code that triggers warnings C4800 and C4385 */ +// GTEST_DISABLE_MSC_WARNINGS_POP_() +#if defined(_MSC_VER) +#define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \ + __pragma(warning(push)) __pragma(warning(disable : warnings)) +#define GTEST_DISABLE_MSC_WARNINGS_POP_() __pragma(warning(pop)) +#else +// Not all compilers are MSVC +#define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) +#define GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Clang on Windows does not understand MSVC's pragma warning. +// We need clang-specific way to disable function deprecation warning. +#ifdef __clang__ +#define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"") +#define GTEST_DISABLE_MSC_DEPRECATED_POP_() _Pragma("clang diagnostic pop") +#else +#define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) +#define GTEST_DISABLE_MSC_DEPRECATED_POP_() GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#ifdef GTEST_OS_WINDOWS +#ifndef GTEST_OS_WINDOWS_MOBILE +#include +#include +#endif +// In order to avoid having to include , use forward declaration +#if defined(GTEST_OS_WINDOWS_MINGW) && !defined(__MINGW64_VERSION_MAJOR) +// MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two +// separate (equivalent) structs, instead of using typedef +typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#else +// Assume CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. +// This assumption is verified by +// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. +typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#endif +#elif defined(GTEST_OS_XTENSA) +#include +// Xtensa toolchains define strcasecmp in the string.h header instead of +// strings.h. string.h is already included. +#else +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +#include +#include +#endif // GTEST_OS_WINDOWS + +#ifdef GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +#include // NOLINT +#endif + +// Defines this to true if and only if Google Test can use POSIX regular +// expressions. +#ifndef GTEST_HAS_POSIX_RE +#ifdef GTEST_OS_LINUX_ANDROID +// On Android, is only available starting with Gingerbread. +#define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +#else +#if !(defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT)) +#define GTEST_HAS_POSIX_RE 1 +#else +#define GTEST_HAS_POSIX_RE 0 +#endif +#endif // GTEST_OS_LINUX_ANDROID +#endif + +// Select the regular expression implementation. +#ifdef GTEST_HAS_ABSL +// When using Abseil, RE2 is required. +#include "absl/strings/string_view.h" +#include "re2/re2.h" +#define GTEST_USES_RE2 1 +#elif GTEST_HAS_POSIX_RE +#include // NOLINT +#define GTEST_USES_POSIX_RE 1 +#else +// Use our own simple regex implementation. +#define GTEST_USES_SIMPLE_RE 1 +#endif + +#ifndef GTEST_HAS_EXCEPTIONS +// The user didn't tell us whether exceptions are enabled, so we need +// to figure it out. +#if defined(_MSC_VER) && defined(_CPPUNWIND) +// MSVC defines _CPPUNWIND to 1 if and only if exceptions are enabled. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__BORLANDC__) +// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS +// macro to enable exceptions, so we'll do the same. +// Assumes that exceptions are enabled by default. +#ifndef _HAS_EXCEPTIONS +#define _HAS_EXCEPTIONS 1 +#endif // _HAS_EXCEPTIONS +#define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +#elif defined(__clang__) +// clang defines __EXCEPTIONS if and only if exceptions are enabled before clang +// 220714, but if and only if cleanups are enabled after that. In Obj-C++ files, +// there can be cleanups for ObjC exceptions which also need cleanups, even if +// C++ exceptions are disabled. clang has __has_feature(cxx_exceptions) which +// checks for C++ exceptions starting at clang r206352, but which checked for +// cleanups prior to that. To reliably check for C++ exception availability with +// clang, check for +// __EXCEPTIONS && __has_feature(cxx_exceptions). +#if defined(__EXCEPTIONS) && __EXCEPTIONS && __has_feature(cxx_exceptions) +#define GTEST_HAS_EXCEPTIONS 1 +#else +#define GTEST_HAS_EXCEPTIONS 0 +#endif +#elif defined(__GNUC__) && defined(__EXCEPTIONS) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__SUNPRO_CC) +// Sun Pro CC supports exceptions. However, there is no compile-time way of +// detecting whether they are enabled or not. Therefore, we assume that +// they are enabled unless the user tells us otherwise. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__IBMCPP__) && defined(__EXCEPTIONS) && __EXCEPTIONS +// xlC defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +#define GTEST_HAS_EXCEPTIONS 1 +#else +// For other compilers, we assume exceptions are disabled to be +// conservative. +#define GTEST_HAS_EXCEPTIONS 0 +#endif // defined(_MSC_VER) || defined(__BORLANDC__) +#endif // GTEST_HAS_EXCEPTIONS + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). +#if (!(defined(GTEST_OS_LINUX_ANDROID) || defined(GTEST_OS_CYGWIN) || \ + defined(GTEST_OS_SOLARIS) || defined(GTEST_OS_HAIKU) || \ + defined(GTEST_OS_ESP32) || defined(GTEST_OS_ESP8266) || \ + defined(GTEST_OS_XTENSA) || defined(GTEST_OS_QURT) || \ + defined(GTEST_OS_NXP_QN9090) || defined(GTEST_OS_NRF52))) +#define GTEST_HAS_STD_WSTRING 1 +#else +#define GTEST_HAS_STD_WSTRING 0 +#endif +#endif // GTEST_HAS_STD_WSTRING + +#ifndef GTEST_HAS_FILE_SYSTEM +// Most platforms support a file system. +#define GTEST_HAS_FILE_SYSTEM 1 +#endif // GTEST_HAS_FILE_SYSTEM + +// Determines whether RTTI is available. +#ifndef GTEST_HAS_RTTI +// The user didn't tell us whether RTTI is enabled, so we need to +// figure it out. + +#ifdef _MSC_VER + +#ifdef _CPPRTTI // MSVC defines this macro if and only if RTTI is enabled. +#define GTEST_HAS_RTTI 1 +#else +#define GTEST_HAS_RTTI 0 +#endif + +// Starting with version 4.3.2, gcc defines __GXX_RTTI if and only if RTTI is +// enabled. +#elif defined(__GNUC__) + +#ifdef __GXX_RTTI +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +#if defined(GTEST_OS_LINUX_ANDROID) && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) +#define GTEST_HAS_RTTI 0 +#else +#define GTEST_HAS_RTTI 1 +#endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS +#else +#define GTEST_HAS_RTTI 0 +#endif // __GXX_RTTI + +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +#elif defined(__clang__) + +#define GTEST_HAS_RTTI __has_feature(cxx_rtti) + +// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if +// both the typeid and dynamic_cast features are present. +#elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) + +#ifdef __RTTI_ALL__ +#define GTEST_HAS_RTTI 1 +#else +#define GTEST_HAS_RTTI 0 +#endif + +#else + +// For all other compilers, we assume RTTI is enabled. +#define GTEST_HAS_RTTI 1 + +#endif // _MSC_VER + +#endif // GTEST_HAS_RTTI + +// It's this header's responsibility to #include when RTTI +// is enabled. +#if GTEST_HAS_RTTI +#include +#endif + +// Determines whether Google Test can use the pthreads library. +#ifndef GTEST_HAS_PTHREAD +// The user didn't tell us explicitly, so we make reasonable assumptions about +// which platforms have pthreads support. +// +// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 +// to your compiler flags. +#if (defined(GTEST_OS_LINUX) || defined(GTEST_OS_MAC) || \ + defined(GTEST_OS_HPUX) || defined(GTEST_OS_QNX) || \ + defined(GTEST_OS_FREEBSD) || defined(GTEST_OS_NACL) || \ + defined(GTEST_OS_NETBSD) || defined(GTEST_OS_FUCHSIA) || \ + defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_GNU_KFREEBSD) || \ + defined(GTEST_OS_OPENBSD) || defined(GTEST_OS_HAIKU) || \ + defined(GTEST_OS_GNU_HURD) || defined(GTEST_OS_SOLARIS) || \ + defined(GTEST_OS_AIX) || defined(GTEST_OS_ZOS)) +#define GTEST_HAS_PTHREAD 1 +#else +#define GTEST_HAS_PTHREAD 0 +#endif +#endif // GTEST_HAS_PTHREAD + +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include when GTEST_HAS_PTHREAD is +// true. +#include // NOLINT + +// For timespec and nanosleep, used below. +#include // NOLINT +#endif + +// Determines whether clone(2) is supported. +// Usually it will only be available on Linux, excluding +// Linux on the Itanium architecture. +// Also see https://linux.die.net/man/2/clone. +#ifndef GTEST_HAS_CLONE +// The user didn't tell us, so we need to figure it out. + +#if defined(GTEST_OS_LINUX) && !defined(__ia64__) +#if defined(GTEST_OS_LINUX_ANDROID) +// On Android, clone() became available at different API levels for each 32-bit +// architecture. +#if defined(__LP64__) || (defined(__arm__) && __ANDROID_API__ >= 9) || \ + (defined(__mips__) && __ANDROID_API__ >= 12) || \ + (defined(__i386__) && __ANDROID_API__ >= 17) +#define GTEST_HAS_CLONE 1 +#else +#define GTEST_HAS_CLONE 0 +#endif +#else +#define GTEST_HAS_CLONE 1 +#endif +#else +#define GTEST_HAS_CLONE 0 +#endif // GTEST_OS_LINUX && !defined(__ia64__) + +#endif // GTEST_HAS_CLONE + +// Determines whether to support stream redirection. This is used to test +// output correctness and to implement death tests. +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile / embedded ones. Also, if the port doesn't have +// a file system, stream redirection is not supported. +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_WINDOWS_PHONE) || \ + defined(GTEST_OS_WINDOWS_RT) || defined(GTEST_OS_WINDOWS_GAMES) || \ + defined(GTEST_OS_ESP8266) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT) || !GTEST_HAS_FILE_SYSTEM +#define GTEST_HAS_STREAM_REDIRECTION 0 +#else +#define GTEST_HAS_STREAM_REDIRECTION 1 +#endif // !GTEST_OS_WINDOWS_MOBILE +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// pops up a dialog window that cannot be suppressed programmatically. +#if (defined(GTEST_OS_LINUX) || defined(GTEST_OS_CYGWIN) || \ + defined(GTEST_OS_SOLARIS) || defined(GTEST_OS_ZOS) || \ + (defined(GTEST_OS_MAC) && !defined(GTEST_OS_IOS)) || \ + (defined(GTEST_OS_WINDOWS_DESKTOP) && _MSC_VER) || \ + defined(GTEST_OS_WINDOWS_MINGW) || defined(GTEST_OS_AIX) || \ + defined(GTEST_OS_HPUX) || defined(GTEST_OS_OPENBSD) || \ + defined(GTEST_OS_QNX) || defined(GTEST_OS_FREEBSD) || \ + defined(GTEST_OS_NETBSD) || defined(GTEST_OS_FUCHSIA) || \ + defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_GNU_KFREEBSD) || \ + defined(GTEST_OS_HAIKU) || defined(GTEST_OS_GNU_HURD)) +// Death tests require a file system to work properly. +#if GTEST_HAS_FILE_SYSTEM +#define GTEST_HAS_DEATH_TEST 1 +#endif // GTEST_HAS_FILE_SYSTEM +#endif + +// Determines whether to support type-driven tests. + +// Typed tests need and variadic macros, which GCC, VC++ 8.0, +// Sun Pro CC, IBM Visual Age, and HP aCC support. +#if defined(__GNUC__) || defined(_MSC_VER) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +#define GTEST_HAS_TYPED_TEST 1 +#define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether the system compiler uses UTF-16 for encoding wide strings. +#if defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_CYGWIN) || \ + defined(GTEST_OS_AIX) || defined(GTEST_OS_OS2) +#define GTEST_WIDE_STRING_USES_UTF16_ 1 +#else +#define GTEST_WIDE_STRING_USES_UTF16_ 0 +#endif + +// Determines whether test results can be streamed to a socket. +#if defined(GTEST_OS_LINUX) || defined(GTEST_OS_GNU_KFREEBSD) || \ + defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_FREEBSD) || \ + defined(GTEST_OS_NETBSD) || defined(GTEST_OS_OPENBSD) || \ + defined(GTEST_OS_GNU_HURD) || defined(GTEST_OS_MAC) +#define GTEST_CAN_STREAM_RESULTS_ 1 +#else +#define GTEST_CAN_STREAM_RESULTS_ 0 +#endif + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +#else +#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + switch (0) \ + case 0: \ + default: // NOLINT +#endif + +// GTEST_HAVE_ATTRIBUTE_ +// +// A function-like feature checking macro that is a wrapper around +// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a +// nonzero constant integer if the attribute is supported or 0 if not. +// +// It evaluates to zero if `__has_attribute` is not defined by the compiler. +// +// GCC: https://gcc.gnu.org/gcc-5/changes.html +// Clang: https://clang.llvm.org/docs/LanguageExtensions.html +#ifdef __has_attribute +#define GTEST_HAVE_ATTRIBUTE_(x) __has_attribute(x) +#else +#define GTEST_HAVE_ATTRIBUTE_(x) 0 +#endif + +// GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE +// +// A function-like feature checking macro that accepts C++11 style attributes. +// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6 +// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't +// find `__has_cpp_attribute`, will evaluate to 0. +#if defined(__has_cpp_attribute) +// NOTE: requiring __cplusplus above should not be necessary, but +// works around https://bugs.llvm.org/show_bug.cgi?id=23435. +#define GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +#define GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE(x) 0 +#endif + +// GTEST_HAVE_FEATURE_ +// +// A function-like feature checking macro that is a wrapper around +// `__has_feature`. +#ifdef __has_feature +#define GTEST_HAVE_FEATURE_(x) __has_feature(x) +#else +#define GTEST_HAVE_FEATURE_(x) 0 +#endif + +// Use this annotation after a variable or parameter declaration to tell the +// compiler the variable/parameter may be used. +// Example: +// +// GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED int foo = bar(); +// +// This can be removed once we only support only C++17 or newer and +// [[maybe_unused]] is available on all supported platforms. +#if GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE(maybe_unused) +#define GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]] +#elif GTEST_HAVE_ATTRIBUTE_(unused) +// This is inferior to [[maybe_unused]] as it can produce a +// -Wused-but-marked-unused warning on optionally used symbols, but it is all we +// have. +#define GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED __attribute__((__unused__)) +#else +#define GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED +#endif + +// Use this annotation before a function that takes a printf format string. +#if GTEST_HAVE_ATTRIBUTE_(format) && defined(__MINGW_PRINTF_FORMAT) +// MinGW has two different printf implementations. Ensure the format macro +// matches the selected implementation. See +// https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/. +#define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((format(__MINGW_PRINTF_FORMAT, string_index, first_to_check))) +#elif GTEST_HAVE_ATTRIBUTE_(format) +#define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((format(printf, string_index, first_to_check))) +#else +#define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) +#endif + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; +#if GTEST_HAVE_ATTRIBUTE_(warn_unused_result) +#define GTEST_MUST_USE_RESULT_ __attribute__((warn_unused_result)) +#else +#define GTEST_MUST_USE_RESULT_ +#endif + +// MS C++ compiler emits warning when a conditional expression is compile time +// constant. In some contexts this warning is false positive and needs to be +// suppressed. Use the following two macros in such cases: +// +// GTEST_INTENTIONAL_CONST_COND_PUSH_() +// while (true) { +// GTEST_INTENTIONAL_CONST_COND_POP_() +// } +#define GTEST_INTENTIONAL_CONST_COND_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4127) +#define GTEST_INTENTIONAL_CONST_COND_POP_() GTEST_DISABLE_MSC_WARNINGS_POP_() + +// Determine whether the compiler supports Microsoft's Structured Exception +// Handling. This is supported by several Windows compilers but generally +// does not exist on any other system. +#ifndef GTEST_HAS_SEH +// The user didn't tell us, so we need to figure it out. + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// These two compilers are known to support SEH. +#define GTEST_HAS_SEH 1 +#else +// Assume no SEH. +#define GTEST_HAS_SEH 0 +#endif + +#endif // GTEST_HAS_SEH + +#ifndef GTEST_IS_THREADSAFE + +#if (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ || \ + (defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT)) || \ + GTEST_HAS_PTHREAD) +#define GTEST_IS_THREADSAFE 1 +#endif + +#endif // GTEST_IS_THREADSAFE + +#ifdef GTEST_IS_THREADSAFE +// Some platforms don't support including these threading related headers. +#include // NOLINT +#include // NOLINT +#endif // GTEST_IS_THREADSAFE + +// GTEST_API_ qualifies all symbols that must be exported. The definitions below +// are guarded by #ifndef to give embedders a chance to define GTEST_API_ in +// gtest/internal/custom/gtest-port.h +#ifndef GTEST_API_ + +#ifdef _MSC_VER +#if defined(GTEST_LINKED_AS_SHARED_LIBRARY) && GTEST_LINKED_AS_SHARED_LIBRARY +#define GTEST_API_ __declspec(dllimport) +#elif defined(GTEST_CREATE_SHARED_LIBRARY) && GTEST_CREATE_SHARED_LIBRARY +#define GTEST_API_ __declspec(dllexport) +#endif +#elif GTEST_HAVE_ATTRIBUTE_(visibility) +#define GTEST_API_ __attribute__((visibility("default"))) +#endif // _MSC_VER + +#endif // GTEST_API_ + +#ifndef GTEST_API_ +#define GTEST_API_ +#endif // GTEST_API_ + +#ifndef GTEST_DEFAULT_DEATH_TEST_STYLE +#define GTEST_DEFAULT_DEATH_TEST_STYLE "fast" +#endif // GTEST_DEFAULT_DEATH_TEST_STYLE + +#if GTEST_HAVE_ATTRIBUTE_(noinline) +// Ask the compiler to never inline a given function. +#define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +#define GTEST_NO_INLINE_ +#endif + +#if GTEST_HAVE_ATTRIBUTE_(disable_tail_calls) +// Ask the compiler not to perform tail call optimization inside +// the marked function. +#define GTEST_NO_TAIL_CALL_ __attribute__((disable_tail_calls)) +#elif defined(__GNUC__) && !defined(__NVCOMPILER) +#define GTEST_NO_TAIL_CALL_ \ + __attribute__((optimize("no-optimize-sibling-calls"))) +#else +#define GTEST_NO_TAIL_CALL_ +#endif + +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if !defined(GTEST_HAS_CXXABI_H_) +#if defined(__GLIBCXX__) || (defined(_LIBCPP_VERSION) && !defined(_MSC_VER)) +#define GTEST_HAS_CXXABI_H_ 1 +#else +#define GTEST_HAS_CXXABI_H_ 0 +#endif +#endif + +// A function level attribute to disable checking for use of uninitialized +// memory when built with MemorySanitizer. +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_memory) +#define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ __attribute__((no_sanitize_memory)) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +#endif + +// A function level attribute to disable AddressSanitizer instrumentation. +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_address) +#define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ \ + __attribute__((no_sanitize_address)) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +#endif + +// A function level attribute to disable HWAddressSanitizer instrumentation. +#if GTEST_HAVE_FEATURE_(hwaddress_sanitizer) && \ + GTEST_HAVE_ATTRIBUTE_(no_sanitize) +#define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ \ + __attribute__((no_sanitize("hwaddress"))) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +#endif + +// A function level attribute to disable ThreadSanitizer instrumentation. +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_thread) +#define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ __attribute((no_sanitize_thread)) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +#endif + +namespace testing { + +class Message; + +// Legacy imports for backwards compatibility. +// New code should use std:: names directly. +using std::get; +using std::make_tuple; +using std::tuple; +using std::tuple_element; +using std::tuple_size; + +namespace internal { + +// A secret type that Google Test users don't know about. It has no +// accessible constructors on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret { + Secret(const Secret&) = delete; +}; + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines RE. + +#ifdef GTEST_USES_RE2 + +// This is almost `using RE = ::RE2`, except it is copy-constructible, and it +// needs to disambiguate the `std::string`, `absl::string_view`, and `const +// char*` constructors. +class GTEST_API_ RE { + public: + RE(absl::string_view regex) : regex_(regex) {} // NOLINT + RE(const char* regex) : RE(absl::string_view(regex)) {} // NOLINT + RE(const std::string& regex) : RE(absl::string_view(regex)) {} // NOLINT + RE(const RE& other) : RE(other.pattern()) {} + + const std::string& pattern() const { return regex_.pattern(); } + + static bool FullMatch(absl::string_view str, const RE& re) { + return RE2::FullMatch(str, re.regex_); + } + static bool PartialMatch(absl::string_view str, const RE& re) { + return RE2::PartialMatch(str, re.regex_); + } + + private: + RE2 regex_; +}; + +#elif defined(GTEST_USES_POSIX_RE) || defined(GTEST_USES_SIMPLE_RE) +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// A simple C++ wrapper for . It uses the POSIX Extended +// Regular Expression syntax. +class GTEST_API_ RE { + public: + // A copy constructor is required by the Standard to initialize object + // references from r-values. + RE(const RE& other) { Init(other.pattern()); } + + // Constructs an RE from a string. + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_.c_str(); } + + // FullMatch(str, re) returns true if and only if regular expression re + // matches the entire str. + // PartialMatch(str, re) returns true if and only if regular expression re + // matches a substring of str (including str itself). + static bool FullMatch(const ::std::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + std::string pattern_; + bool is_valid_; + +#ifdef GTEST_USES_POSIX_RE + + regex_t full_regex_; // For FullMatch(). + regex_t partial_regex_; // For PartialMatch(). + +#else // GTEST_USES_SIMPLE_RE + + std::string full_pattern_; // For FullMatch(); + +#endif +}; +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 +#endif // ::testing::internal::RE implementation + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + +// Defines logging utilities: +// GTEST_LOG_(severity) - logs messages at the specified severity level. The +// message itself is streamed into the macro. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { GTEST_INFO, GTEST_WARNING, GTEST_ERROR, GTEST_FATAL }; + +// Formats log entry severity, provides a stream object for streaming the +// log message, and terminates the message with a newline when going out of +// scope. +class GTEST_API_ GTestLog { + public: + GTestLog(GTestLogSeverity severity, const char* file, int line); + + // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. + ~GTestLog(); + + ::std::ostream& GetStream() { return ::std::cerr; } + + private: + const GTestLogSeverity severity_; + + GTestLog(const GTestLog&) = delete; + GTestLog& operator=(const GTestLog&) = delete; +}; + +#if !defined(GTEST_LOG_) + +#define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__) \ + .GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(nullptr); } + +#endif // !defined(GTEST_LOG_) + +#if !defined(GTEST_CHECK_) +// INTERNAL IMPLEMENTATION - DO NOT USE. +// +// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition +// is not satisfied. +// Synopsis: +// GTEST_CHECK_(boolean_condition); +// or +// GTEST_CHECK_(boolean_condition) << "Additional message"; +// +// This checks the condition and if the condition is not satisfied +// it prints message about the condition violation, including the +// condition itself, plus additional message streamed into it, if any, +// and then it aborts the program. It aborts the program irrespective of +// whether it is built in the debug mode or not. +#define GTEST_CHECK_(condition) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::IsTrue(condition)) \ + ; \ + else \ + GTEST_LOG_(FATAL) << "Condition " #condition " failed. " +#endif // !defined(GTEST_CHECK_) + +// An all-mode assert to verify that the given POSIX-style function +// call returns 0 (indicating success). Known limitation: this +// doesn't expand to a balanced 'if' statement, so enclose the macro +// in {} if you need to use it as the only statement in an 'if' +// branch. +#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ + if (const int gtest_error = (posix_call)) \ + GTEST_LOG_(FATAL) << #posix_call << "failed with error " << gtest_error + +// Transforms "T" into "const T&" according to standard reference collapsing +// rules (this is only needed as a backport for C++98 compilers that do not +// support reference collapsing). Specifically, it transforms: +// +// char ==> const char& +// const char ==> const char& +// char& ==> char& +// const char& ==> const char& +// +// Note that the non-const reference will not have "const" added. This is +// standard, and necessary so that "T" can always bind to "const T&". +template +struct ConstRef { + typedef const T& type; +}; +template +struct ConstRef { + typedef T& type; +}; + +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + typename ::testing::internal::ConstRef::type + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertible to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template +inline To ImplicitCast_(To x) { + return x; +} + +// Downcasts the pointer of type Base to Derived. +// Derived must be a subclass of Base. The parameter MUST +// point to a class of type Derived, not any subclass of it. +// When RTTI is available, the function performs a runtime +// check to enforce this. +template +Derived* CheckedDowncastToActualType(Base* base) { + static_assert(std::is_base_of::value, + "target type not derived from source type"); +#if GTEST_HAS_RTTI + GTEST_CHECK_(base == nullptr || dynamic_cast(base) != nullptr); +#endif + return static_cast(base); +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Defines the stderr capturer: +// CaptureStdout - starts capturing stdout. +// GetCapturedStdout - stops capturing stdout and returns the captured string. +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. +// +GTEST_API_ void CaptureStdout(); +GTEST_API_ std::string GetCapturedStdout(); +GTEST_API_ void CaptureStderr(); +GTEST_API_ std::string GetCapturedStderr(); + +#endif // GTEST_HAS_STREAM_REDIRECTION +// Returns the size (in bytes) of a file. +GTEST_API_ size_t GetFileSize(FILE* file); + +// Reads the entire content of a file as a string. +GTEST_API_ std::string ReadEntireFile(FILE* file); + +// All command line arguments. +GTEST_API_ std::vector GetArgvs(); + +#ifdef GTEST_HAS_DEATH_TEST + +std::vector GetInjectableArgvs(); +// Deprecated: pass the args vector by value instead. +void SetInjectableArgvs(const std::vector* new_argvs); +void SetInjectableArgvs(const std::vector& new_argvs); +void ClearInjectableArgvs(); + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. +#ifdef GTEST_IS_THREADSAFE + +#ifdef GTEST_OS_WINDOWS +// Provides leak-safe Windows kernel handle ownership. +// Used in death tests and in threading support. +class GTEST_API_ AutoHandle { + public: + // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to + // avoid including in this header file. Including is + // undesirable because it defines a lot of symbols and macros that tend to + // conflict with client code. This assumption is verified by + // WindowsTypesTest.HANDLEIsVoidStar. + typedef void* Handle; + AutoHandle(); + explicit AutoHandle(Handle handle); + + ~AutoHandle(); + + Handle Get() const; + void Reset(); + void Reset(Handle handle); + + private: + // Returns true if and only if the handle is a valid handle object that can be + // closed. + bool IsCloseable() const; + + Handle handle_; + + AutoHandle(const AutoHandle&) = delete; + AutoHandle& operator=(const AutoHandle&) = delete; +}; +#endif + +#if GTEST_HAS_NOTIFICATION_ +// Notification has already been imported into the namespace. +// Nothing to do here. + +#else +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +// TODO(b/203539622): Replace unconditionally with absl::Notification. +class GTEST_API_ Notification { + public: + Notification() : notified_(false) {} + Notification(const Notification&) = delete; + Notification& operator=(const Notification&) = delete; + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + std::lock_guard lock(mu_); + notified_ = true; + cv_.notify_all(); + } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + std::unique_lock lock(mu_); + cv_.wait(lock, [this]() { return notified_; }); + } + + private: + std::mutex mu_; + std::condition_variable cv_; + bool notified_; +}; +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 +#endif // GTEST_HAS_NOTIFICATION_ + +// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD +// defined, but we don't want to use MinGW's pthreads implementation, which +// has conformance problems with some versions of the POSIX standard. +#if GTEST_HAS_PTHREAD && !defined(GTEST_OS_WINDOWS_MINGW) + +// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. +// Consequently, it cannot select a correct instantiation of ThreadWithParam +// in order to call its Run(). Introducing ThreadWithParamBase as a +// non-templated base class for ThreadWithParam allows us to bypass this +// problem. +class ThreadWithParamBase { + public: + virtual ~ThreadWithParamBase() = default; + virtual void Run() = 0; +}; + +// pthread_create() accepts a pointer to a function type with the C linkage. +// According to the Standard (7.5/1), function types with different linkages +// are different even if they are otherwise identical. Some compilers (for +// example, SunStudio) treat them as different types. Since class methods +// cannot be defined with C-linkage we need to define a free C-function to +// pass into pthread_create(). +extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { + static_cast(thread)->Run(); + return nullptr; +} + +// Helper class for testing Google Test's multi-threading constructs. +// To use it, write: +// +// void ThreadFunc(int param) { /* Do things with param */ } +// Notification thread_can_start; +// ... +// // The thread_can_start parameter is optional; you can supply NULL. +// ThreadWithParam thread(&ThreadFunc, 5, &thread_can_start); +// thread_can_start.Notify(); +// +// These classes are only for testing Google Test's own constructs. Do +// not use them in user tests, either directly or indirectly. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : func_(func), + param_(param), + thread_can_start_(thread_can_start), + finished_(false) { + ThreadWithParamBase* const base = this; + // The thread can be created only after all fields except thread_ + // have been initialized. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_create(&thread_, nullptr, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() override { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, nullptr)); + finished_ = true; + } + } + + void Run() override { + if (thread_can_start_ != nullptr) thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + UserThreadFunc* const func_; // User-supplied thread function. + const T param_; // User-supplied parameter to the thread function. + // When non-NULL, used to block execution until the controller thread + // notifies. + Notification* const thread_can_start_; + bool finished_; // true if and only if we know that the thread function has + // finished. + pthread_t thread_; // The native thread object. + + ThreadWithParam(const ThreadWithParam&) = delete; + ThreadWithParam& operator=(const ThreadWithParam&) = delete; +}; +#endif // !GTEST_OS_WINDOWS && GTEST_HAS_PTHREAD || + // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +#if GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ +// Mutex and ThreadLocal have already been imported into the namespace. +// Nothing to do here. + +#elif defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT) + +// Mutex implements mutex on Windows platforms. It is used in conjunction +// with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the +// // end of the current scope. +// +// A static Mutex *must* be defined or declared using one of the following +// macros: +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// (A non-static Mutex is defined/declared in the usual way). +class GTEST_API_ Mutex { + public: + enum MutexType { kStatic = 0, kDynamic = 1 }; + // We rely on kStaticMutex being 0 as it is to what the linker initializes + // type_ in static mutexes. critical_section_ will be initialized lazily + // in ThreadSafeLazyInit(). + enum StaticConstructorSelector { kStaticMutex = 0 }; + + // This constructor intentionally does nothing. It relies on type_ being + // statically initialized to 0 (effectively setting it to kStatic) and on + // ThreadSafeLazyInit() to lazily initialize the rest of the members. + explicit Mutex(StaticConstructorSelector /*dummy*/) {} + + Mutex(); + ~Mutex(); + + void Lock(); + + void Unlock(); + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld(); + + private: + // Initializes owner_thread_id_ and critical_section_ in static mutexes. + void ThreadSafeLazyInit(); + + // Per https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503, + // we assume that 0 is an invalid value for thread IDs. + unsigned int owner_thread_id_; + + // For static mutexes, we rely on these members being initialized to zeros + // by the linker. + MutexType type_; + long critical_section_init_phase_; // NOLINT + GTEST_CRITICAL_SECTION* critical_section_; + + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; +}; + +#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::Mutex mutex(::testing::internal::Mutex::kStaticMutex) + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex* mutex) : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + Mutex* const mutex_; + + GTestMutexLock(const GTestMutexLock&) = delete; + GTestMutexLock& operator=(const GTestMutexLock&) = delete; +}; + +typedef GTestMutexLock MutexLock; + +// Base class for ValueHolder. Allows a caller to hold and delete a value +// without knowing its type. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Provides a way for a thread to send notifications to a ThreadLocal +// regardless of its parameter type. +class ThreadLocalBase { + public: + // Creates a new ValueHolder object holding a default value passed to + // this ThreadLocal's constructor and returns it. It is the caller's + // responsibility not to call this when the ThreadLocal instance already + // has a value on the current thread. + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const = 0; + + protected: + ThreadLocalBase() {} + virtual ~ThreadLocalBase() {} + + private: + ThreadLocalBase(const ThreadLocalBase&) = delete; + ThreadLocalBase& operator=(const ThreadLocalBase&) = delete; +}; + +// Maps a thread to a set of ThreadLocals that have values instantiated on that +// thread and notifies them when the thread exits. A ThreadLocal instance is +// expected to persist until all threads it has values on have terminated. +class GTEST_API_ ThreadLocalRegistry { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance); + + // Invoked when a ThreadLocal instance is destroyed. + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance); +}; + +class GTEST_API_ ThreadWithParamBase { + public: + void Join(); + + protected: + class Runnable { + public: + virtual ~Runnable() {} + virtual void Run() = 0; + }; + + ThreadWithParamBase(Runnable* runnable, Notification* thread_can_start); + virtual ~ThreadWithParamBase(); + + private: + AutoHandle thread_; +}; + +// Helper class for testing Google Test's multi-threading constructs. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : ThreadWithParamBase(new RunnableImpl(func, param), thread_can_start) {} + virtual ~ThreadWithParam() {} + + private: + class RunnableImpl : public Runnable { + public: + RunnableImpl(UserThreadFunc* func, T param) : func_(func), param_(param) {} + virtual ~RunnableImpl() {} + virtual void Run() { func_(param_); } + + private: + UserThreadFunc* const func_; + const T param_; + + RunnableImpl(const RunnableImpl&) = delete; + RunnableImpl& operator=(const RunnableImpl&) = delete; + }; + + ThreadWithParam(const ThreadWithParam&) = delete; + ThreadWithParam& operator=(const ThreadWithParam&) = delete; +}; + +// Implements thread-local storage on Windows systems. +// +// // Thread 1 +// ThreadLocal tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// The users of a TheadLocal instance have to make sure that all but one +// threads (including the main one) using that instance have exited before +// destroying it. Otherwise, the per-thread objects managed for them by the +// ThreadLocal instance are not guaranteed to be destroyed on all platforms. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template +class ThreadLocal : public ThreadLocalBase { + public: + ThreadLocal() : default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() override { ThreadLocalRegistry::OnThreadLocalDestroyed(this); } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of T. Can be deleted via its base class without the caller + // knowing the type of T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + ValueHolder(const ValueHolder&) = delete; + ValueHolder& operator=(const ValueHolder&) = delete; + }; + + T* GetOrCreateValue() const { + return static_cast( + ThreadLocalRegistry::GetValueOnCurrentThread(this)) + ->pointer(); + } + + ThreadLocalValueHolderBase* NewValueForCurrentThread() const override { + return default_factory_->MakeNewHolder(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + ValueHolderFactory(const ValueHolderFactory&) = delete; + ValueHolderFactory& operator=(const ValueHolderFactory&) = delete; + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + DefaultValueHolderFactory(const DefaultValueHolderFactory&) = delete; + DefaultValueHolderFactory& operator=(const DefaultValueHolderFactory&) = + delete; + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + InstanceValueHolderFactory(const InstanceValueHolderFactory&) = delete; + InstanceValueHolderFactory& operator=(const InstanceValueHolderFactory&) = + delete; + }; + + std::unique_ptr default_factory_; + + ThreadLocal(const ThreadLocal&) = delete; + ThreadLocal& operator=(const ThreadLocal&) = delete; +}; + +#elif GTEST_HAS_PTHREAD + +// MutexBase and Mutex implement mutex on pthreads-based platforms. +class MutexBase { + public: + // Acquires this mutex. + void Lock() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); + owner_ = pthread_self(); + has_owner_ = true; + } + + // Releases this mutex. + void Unlock() { + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + has_owner_ = false; + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); + } + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld() const { + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) + << "The current thread is not holding the mutex @" << this; + } + + // A static mutex may be used before main() is entered. It may even + // be used before the dynamic initialization stage. Therefore we + // must be able to initialize a static mutex object at link time. + // This means MutexBase has to be a POD and its member variables + // have to be public. + public: + pthread_mutex_t mutex_; // The underlying pthread mutex. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. +}; + +// Forward-declares a static mutex. +#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex + +// Defines and statically (i.e. at link time) initializes a static mutex. +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = {PTHREAD_MUTEX_INITIALIZER, false, 0} + +// The Mutex class can only be used for mutexes created at runtime. It +// shares its API with MutexBase otherwise. +class Mutex : public MutexBase { + public: + Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr)); + has_owner_ = false; + } + ~Mutex() { GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); } + + private: + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; +}; + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(MutexBase* mutex) : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + MutexBase* const mutex_; + + GTestMutexLock(const GTestMutexLock&) = delete; + GTestMutexLock& operator=(const GTestMutexLock&) = delete; +}; + +typedef GTestMutexLock MutexLock; + +// Helpers for ThreadLocal. + +// pthread_key_create() requires DeleteThreadLocalValue() to have +// C-linkage. Therefore it cannot be templatized to access +// ThreadLocal. Hence the need for class +// ThreadLocalValueHolderBase. +class GTEST_API_ ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() = default; +}; + +// Called by pthread to delete thread-local data stored by +// pthread_setspecific(). +extern "C" inline void DeleteThreadLocalValue(void* value_holder) { + delete static_cast(value_holder); +} + +// Implements thread-local storage on pthreads-based systems. +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() + : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : key_(CreateKey()), + default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() { + // Destroys the managed object for the current thread, if any. + DeleteThreadLocalValue(pthread_getspecific(key_)); + + // Releases resources associated with the key. This will *not* + // delete managed objects for other threads. + GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); + } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of type T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + ValueHolder(const ValueHolder&) = delete; + ValueHolder& operator=(const ValueHolder&) = delete; + }; + + static pthread_key_t CreateKey() { + pthread_key_t key; + // When a thread exits, DeleteThreadLocalValue() will be called on + // the object managed for that thread. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_key_create(&key, &DeleteThreadLocalValue)); + return key; + } + + T* GetOrCreateValue() const { + ThreadLocalValueHolderBase* const holder = + static_cast(pthread_getspecific(key_)); + if (holder != nullptr) { + return CheckedDowncastToActualType(holder)->pointer(); + } + + ValueHolder* const new_holder = default_factory_->MakeNewHolder(); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() = default; + virtual ~ValueHolderFactory() = default; + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + ValueHolderFactory(const ValueHolderFactory&) = delete; + ValueHolderFactory& operator=(const ValueHolderFactory&) = delete; + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() = default; + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + DefaultValueHolderFactory(const DefaultValueHolderFactory&) = delete; + DefaultValueHolderFactory& operator=(const DefaultValueHolderFactory&) = + delete; + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + InstanceValueHolderFactory(const InstanceValueHolderFactory&) = delete; + InstanceValueHolderFactory& operator=(const InstanceValueHolderFactory&) = + delete; + }; + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + std::unique_ptr default_factory_; + + ThreadLocal(const ThreadLocal&) = delete; + ThreadLocal& operator=(const ThreadLocal&) = delete; +}; + +#endif // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +#else // GTEST_IS_THREADSAFE + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + void Lock() {} + void Unlock() {} + void AssertHeld() const {} +}; + +#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() : value_() {} + explicit ThreadLocal(const T& value) : value_(value) {} + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + + private: + T value_; +}; + +#endif // GTEST_IS_THREADSAFE + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +#ifdef GTEST_OS_WINDOWS +#define GTEST_PATH_SEP_ "\\" +#define GTEST_HAS_ALT_PATH_SEP_ 1 +#else +#define GTEST_PATH_SEP_ "/" +#define GTEST_HAS_ALT_PATH_SEP_ 0 +#endif // GTEST_OS_WINDOWS + +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast(ch)) != 0; +} +#ifdef __cpp_lib_char8_t +inline bool IsXDigit(char8_t ch) { + return isxdigit(static_cast(ch)) != 0; +} +#endif +inline bool IsXDigit(char16_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(char32_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} + +inline char ToLower(char ch) { + return static_cast(tolower(static_cast(ch))); +} +inline char ToUpper(char ch) { + return static_cast(toupper(static_cast(ch))); +} + +inline std::string StripTrailingSpaces(std::string str) { + std::string::iterator it = str.end(); + while (it != str.begin() && IsSpace(*--it)) it = str.erase(it); + return str; +} + +// The testing::internal::posix namespace holds wrappers for common +// POSIX functions. These wrappers hide the differences between +// Windows/MSVC and POSIX systems. Since some compilers define these +// standard functions as macros, the wrapper cannot have the same name +// as the wrapped function. + +namespace posix { + +// File system porting. +// Note: Not every I/O-related function is related to file systems, so don't +// just disable all of them here. For example, fileno() and isatty(), etc. must +// always be available in order to detect if a pipe points to a terminal. +#ifdef GTEST_OS_WINDOWS + +typedef struct _stat StatStruct; + +#ifdef GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +#else +inline int FileNo(FILE* file) { return _fileno(file); } +#if GTEST_HAS_FILE_SYSTEM +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return (_S_IFDIR & st.st_mode) != 0; } +#endif +#endif // GTEST_OS_WINDOWS_MOBILE + +#elif defined(GTEST_OS_ESP8266) +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +#if GTEST_HAS_FILE_SYSTEM +inline int Stat(const char* path, StatStruct* buf) { + // stat function not implemented on ESP8266 + return 0; +} +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } +#endif + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +#if GTEST_HAS_FILE_SYSTEM +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +#ifdef GTEST_OS_QURT +// QuRT doesn't support any directory functions, including rmdir +inline int RmDir(const char*) { return 0; } +#else +inline int RmDir(const char* dir) { return rmdir(dir); } +#endif +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } +#endif + +#endif // GTEST_OS_WINDOWS + +// Other functions with a different name on Windows. + +#ifdef GTEST_OS_WINDOWS + +#ifdef __BORLANDC__ +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +#else // !__BORLANDC__ +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_ZOS) || \ + defined(GTEST_OS_IOS) || defined(GTEST_OS_WINDOWS_PHONE) || \ + defined(GTEST_OS_WINDOWS_RT) || defined(ESP_PLATFORM) +inline int DoIsATTY(int /* fd */) { return 0; } +#else +inline int DoIsATTY(int fd) { return _isatty(fd); } +#endif // GTEST_OS_WINDOWS_MOBILE +inline int StrCaseCmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +#endif // __BORLANDC__ + +#else + +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} + +#endif // GTEST_OS_WINDOWS + +inline int IsATTY(int fd) { + // DoIsATTY might change errno (for example ENOTTY in case you redirect stdout + // to a file on Linux), which is unexpected, so save the previous value, and + // restore it after the call. + int savedErrno = errno; + int isAttyValue = DoIsATTY(fd); + errno = savedErrno; + + return isAttyValue; +} + +// Functions deprecated by MSVC 8.0. + +GTEST_DISABLE_MSC_DEPRECATED_PUSH_() + +// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and +// StrError() aren't needed on Windows CE at this time and thus not +// defined there. +#if GTEST_HAS_FILE_SYSTEM +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT) && !defined(GTEST_OS_WINDOWS_GAMES) && \ + !defined(GTEST_OS_ESP8266) && !defined(GTEST_OS_XTENSA) && \ + !defined(GTEST_OS_QURT) +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { +#if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_MINGW) + struct wchar_codecvt : public std::codecvt {}; + std::wstring_convert converter; + std::wstring wide_path = converter.from_bytes(path); + std::wstring wide_mode = converter.from_bytes(mode); + return _wfopen(wide_path.c_str(), wide_mode.c_str()); +#else // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + return fopen(path, mode); +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW +} +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_QURT) +inline FILE* FReopen(const char* path, const char* mode, FILE* stream) { + return freopen(path, mode, stream); +} +inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } +#endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_QURT +inline int FClose(FILE* fp) { return fclose(fp); } +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_QURT) +inline int Read(int fd, void* buf, unsigned int count) { + return static_cast(read(fd, buf, count)); +} +inline int Write(int fd, const void* buf, unsigned int count) { + return static_cast(write(fd, buf, count)); +} +inline int Close(int fd) { return close(fd); } +#endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_QURT +#endif // GTEST_HAS_FILE_SYSTEM + +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_QURT) +inline const char* StrError(int errnum) { return strerror(errnum); } +#endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_QURT + +inline const char* GetEnv(const char* name) { +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_WINDOWS_PHONE) || \ + defined(GTEST_OS_ESP8266) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT) + // We are on an embedded platform, which has no environment variables. + static_cast(name); // To prevent 'unused argument' warning. + return nullptr; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // Environment variables which we programmatically clear will be set to the + // empty string rather than unset (NULL). Handle that case. + const char* const env = getenv(name); + return (env != nullptr && env[0] != '\0') ? env : nullptr; +#else + return getenv(name); +#endif +} + +GTEST_DISABLE_MSC_DEPRECATED_POP_() + +#ifdef GTEST_OS_WINDOWS_MOBILE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +[[noreturn]] void Abort(); +#else +[[noreturn]] inline void Abort() { abort(); } +#endif // GTEST_OS_WINDOWS_MOBILE + +} // namespace posix + +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if defined(_MSC_VER) && !defined(GTEST_OS_WINDOWS_MOBILE) +// MSVC 2005 and above support variadic macros. +#define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s +#define GTEST_SNPRINTF_ _snprintf +#else +#define GTEST_SNPRINTF_ snprintf +#endif + +// The biggest signed integer type the compiler supports. +// +// long long is guaranteed to be at least 64-bits in C++11. +using BiggestInt = long long; // NOLINT + +// The maximum number a BiggestInt can represent. +constexpr BiggestInt kMaxBiggestInt = (std::numeric_limits::max)(); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize with incorrect + // values of N. + using UInt = void; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + using Int = std::int32_t; + using UInt = std::uint32_t; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: + using Int = std::int64_t; + using UInt = std::uint64_t; +}; + +// Integer types of known sizes. +using TimeInMillis = int64_t; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#if !defined(GTEST_FLAG) +#define GTEST_FLAG_NAME_(name) gtest_##name +#define GTEST_FLAG(name) FLAGS_gtest_##name +#endif // !defined(GTEST_FLAG) + +// Pick a command line flags implementation. +#ifdef GTEST_INTERNAL_HAS_ABSL_FLAGS + +// Macros for defining flags. +#define GTEST_DEFINE_bool_(name, default_val, doc) \ + ABSL_FLAG(bool, GTEST_FLAG_NAME_(name), default_val, doc) +#define GTEST_DEFINE_int32_(name, default_val, doc) \ + ABSL_FLAG(int32_t, GTEST_FLAG_NAME_(name), default_val, doc) +#define GTEST_DEFINE_string_(name, default_val, doc) \ + ABSL_FLAG(std::string, GTEST_FLAG_NAME_(name), default_val, doc) + +// Macros for declaring flags. +#define GTEST_DECLARE_bool_(name) \ + ABSL_DECLARE_FLAG(bool, GTEST_FLAG_NAME_(name)) +#define GTEST_DECLARE_int32_(name) \ + ABSL_DECLARE_FLAG(int32_t, GTEST_FLAG_NAME_(name)) +#define GTEST_DECLARE_string_(name) \ + ABSL_DECLARE_FLAG(std::string, GTEST_FLAG_NAME_(name)) + +#define GTEST_FLAG_SAVER_ ::absl::FlagSaver + +#define GTEST_FLAG_GET(name) ::absl::GetFlag(GTEST_FLAG(name)) +#define GTEST_FLAG_SET(name, value) \ + (void)(::absl::SetFlag(>EST_FLAG(name), value)) +#define GTEST_USE_OWN_FLAGFILE_FLAG_ 0 + +#undef GTEST_INTERNAL_HAS_ABSL_FLAGS +#else // ndef GTEST_INTERNAL_HAS_ABSL_FLAGS + +// Macros for defining flags. +#define GTEST_DEFINE_bool_(name, default_val, doc) \ + namespace testing { \ + GTEST_API_ bool GTEST_FLAG(name) = (default_val); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DEFINE_int32_(name, default_val, doc) \ + namespace testing { \ + GTEST_API_ std::int32_t GTEST_FLAG(name) = (default_val); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DEFINE_string_(name, default_val, doc) \ + namespace testing { \ + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val); \ + } \ + static_assert(true, "no-op to require trailing semicolon") + +// Macros for declaring flags. +#define GTEST_DECLARE_bool_(name) \ + namespace testing { \ + GTEST_API_ extern bool GTEST_FLAG(name); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DECLARE_int32_(name) \ + namespace testing { \ + GTEST_API_ extern std::int32_t GTEST_FLAG(name); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DECLARE_string_(name) \ + namespace testing { \ + GTEST_API_ extern ::std::string GTEST_FLAG(name); \ + } \ + static_assert(true, "no-op to require trailing semicolon") + +#define GTEST_FLAG_SAVER_ ::testing::internal::GTestFlagSaver + +#define GTEST_FLAG_GET(name) ::testing::GTEST_FLAG(name) +#define GTEST_FLAG_SET(name, value) (void)(::testing::GTEST_FLAG(name) = value) +#define GTEST_USE_OWN_FLAGFILE_FLAG_ 1 + +#endif // GTEST_INTERNAL_HAS_ABSL_FLAGS + +// Thread annotations +#if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) +#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +#define GTEST_LOCK_EXCLUDED_(locks) +#endif // !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +GTEST_API_ bool ParseInt32(const Message& src_text, const char* str, + int32_t* value); + +// Parses a bool/int32_t/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ int32_t Int32FromGTestEnv(const char* flag, int32_t default_val); +std::string OutputFlagAlsoCheckEnvVar(); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#if !defined(GTEST_INTERNAL_DEPRECATED) + +// Internal Macro to mark an API deprecated, for googletest usage only +// Usage: class GTEST_INTERNAL_DEPRECATED(message) MyClass or +// GTEST_INTERNAL_DEPRECATED(message) myFunction(); Every usage of +// a deprecated entity will trigger a warning when compiled with +// `-Wdeprecated-declarations` option (clang, gcc, any __GNUC__ compiler). +// For msvc /W3 option will need to be used +// Note that for 'other' compilers this macro evaluates to nothing to prevent +// compilations errors. +#if defined(_MSC_VER) +#define GTEST_INTERNAL_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__GNUC__) +#define GTEST_INTERNAL_DEPRECATED(message) __attribute__((deprecated(message))) +#else +#define GTEST_INTERNAL_DEPRECATED(message) +#endif + +#endif // !defined(GTEST_INTERNAL_DEPRECATED) + +#ifdef GTEST_HAS_ABSL +// Always use absl::any for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_ANY 1 +#include "absl/types/any.h" +namespace testing { +namespace internal { +using Any = ::absl::any; +} // namespace internal +} // namespace testing +#else +#if defined(__cpp_lib_any) || (GTEST_INTERNAL_HAS_INCLUDE() && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L && \ + (!defined(_MSC_VER) || GTEST_HAS_RTTI)) +// Otherwise for C++17 and higher use std::any for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_ANY 1 +#include +namespace testing { +namespace internal { +using Any = ::std::any; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::any is not +// supported. +#endif // __cpp_lib_any +#endif // GTEST_HAS_ABSL + +#ifndef GTEST_INTERNAL_HAS_ANY +#define GTEST_INTERNAL_HAS_ANY 0 +#endif + +#ifdef GTEST_HAS_ABSL +// Always use absl::optional for UniversalPrinter<> specializations if +// googletest is built with absl support. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include "absl/types/optional.h" +namespace testing { +namespace internal { +template +using Optional = ::absl::optional; +inline ::absl::nullopt_t Nullopt() { return ::absl::nullopt; } +} // namespace internal +} // namespace testing +#else +#if defined(__cpp_lib_optional) || (GTEST_INTERNAL_HAS_INCLUDE() && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L) +// Otherwise for C++17 and higher use std::optional for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include +namespace testing { +namespace internal { +template +using Optional = ::std::optional; +inline ::std::nullopt_t Nullopt() { return ::std::nullopt; } +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::optional is not +// supported. +#endif // __cpp_lib_optional +#endif // GTEST_HAS_ABSL + +#ifndef GTEST_INTERNAL_HAS_OPTIONAL +#define GTEST_INTERNAL_HAS_OPTIONAL 0 +#endif + +#if defined(__cpp_lib_span) || (GTEST_INTERNAL_HAS_INCLUDE() && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 202002L) +#define GTEST_INTERNAL_HAS_STD_SPAN 1 +#endif // __cpp_lib_span + +#ifndef GTEST_INTERNAL_HAS_STD_SPAN +#define GTEST_INTERNAL_HAS_STD_SPAN 0 +#endif + +#ifdef GTEST_HAS_ABSL +// Always use absl::string_view for Matcher<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include "absl/strings/string_view.h" +namespace testing { +namespace internal { +using StringView = ::absl::string_view; +} // namespace internal +} // namespace testing +#else +#if defined(__cpp_lib_string_view) || \ + (GTEST_INTERNAL_HAS_INCLUDE() && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L) +// Otherwise for C++17 and higher use std::string_view for Matcher<> +// specializations. +#define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include +namespace testing { +namespace internal { +using StringView = ::std::string_view; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::string_view is not +// supported. +#endif // __cpp_lib_string_view +#endif // GTEST_HAS_ABSL + +#ifndef GTEST_INTERNAL_HAS_STRING_VIEW +#define GTEST_INTERNAL_HAS_STRING_VIEW 0 +#endif + +#ifdef GTEST_HAS_ABSL +// Always use absl::variant for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include "absl/types/variant.h" +namespace testing { +namespace internal { +template +using Variant = ::absl::variant; +} // namespace internal +} // namespace testing +#else +#if defined(__cpp_lib_variant) || (GTEST_INTERNAL_HAS_INCLUDE() && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L) +// Otherwise for C++17 and higher use std::variant for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include +namespace testing { +namespace internal { +template +using Variant = ::std::variant; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::variant is not supported. +#endif // __cpp_lib_variant +#endif // GTEST_HAS_ABSL + +#ifndef GTEST_INTERNAL_HAS_VARIANT +#define GTEST_INTERNAL_HAS_VARIANT 0 +#endif + +#if (defined(__cpp_constexpr) && !defined(__cpp_inline_variables)) || \ + (defined(GTEST_INTERNAL_CPLUSPLUS_LANG) && \ + GTEST_INTERNAL_CPLUSPLUS_LANG < 201703L) +#define GTEST_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL 1 +#endif + +#if (defined(__cpp_lib_three_way_comparison) || \ + (GTEST_INTERNAL_HAS_INCLUDE() && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 201907L)) +#define GTEST_INTERNAL_HAS_COMPARE_LIB 1 +#else +#define GTEST_INTERNAL_HAS_COMPARE_LIB 0 +#endif + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/gtest-string.h b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-string.h new file mode 100644 index 0000000..7c05b58 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-string.h @@ -0,0 +1,178 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by gtest-internal.h. +// It should not be #included by other files. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +#include +#endif + +#include + +#include +#include +#include + +#include "gtest/internal/gtest-port.h" + +namespace testing { +namespace internal { + +// String - an abstract class holding static string utilities. +class GTEST_API_ String { + public: + // Static utility methods + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#ifdef GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true if and only if they have the same + // content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static std::string ShowWideCString(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true if and only if they have the + // same content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Returns true if and only if the given string ends with the given suffix, + // ignoring case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive(const std::string& str, + const std::string& suffix); + + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 + + // Formats an int value to given width with leading zeros. + static std::string FormatIntWidthN(int value, int width); + + // Formats an int value as "%X". + static std::string FormatHexInt(int value); + + // Formats an int value as "%X". + static std::string FormatHexUInt32(uint32_t value); + + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); + + private: + String(); // Not meant to be instantiated. +}; // class String + +// Gets the content of the stringstream's buffer as an std::string. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ diff --git a/Tests/ThirdParty/googletest/include/gtest/internal/gtest-type-util.h b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-type-util.h new file mode 100644 index 0000000..78da053 --- /dev/null +++ b/Tests/ThirdParty/googletest/include/gtest/internal/gtest-type-util.h @@ -0,0 +1,220 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Type utilities needed for implementing typed and type-parameterized +// tests. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +#include +#include +#include + +#include "gtest/internal/gtest-port.h" + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +#if GTEST_HAS_CXXABI_H_ +#include +#elif defined(__HP_aCC) +#include +#endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// Canonicalizes a given name with respect to the Standard C++ Library. +// This handles removing the inline namespace within `std` that is +// used by various standard libraries (e.g., `std::__1`). Names outside +// of namespace std are returned unmodified. +inline std::string CanonicalizeForStdLibVersioning(std::string s) { + static const char prefix[] = "std::__"; + if (s.compare(0, strlen(prefix), prefix) == 0) { + std::string::size_type end = s.find("::", strlen(prefix)); + if (end != s.npos) { + // Erase everything between the initial `std` and the second `::`. + s.erase(strlen("std"), end - strlen("std")); + } + } + + // Strip redundant spaces in typename to match MSVC + // For example, std::pair -> std::pair + static const char to_search[] = ", "; + const char replace_char = ','; + size_t pos = 0; + while (true) { + // Get the next occurrence from the current position + pos = s.find(to_search, pos); + if (pos == std::string::npos) { + break; + } + // Replace this occurrence of substring + s.replace(pos, strlen(to_search), 1, replace_char); + ++pos; + } + return s; +} + +#if GTEST_HAS_RTTI +// GetTypeName(const std::type_info&) returns a human-readable name of type T. +inline std::string GetTypeName(const std::type_info& type) { + const char* const name = type.name(); +#if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +#if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +#endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return CanonicalizeForStdLibVersioning(name_str); +#elif defined(_MSC_VER) + // Strip struct and class due to differences between + // MSVC and other compilers. std::pair is printed as + // "struct std::pair" when using MSVC vs "std::pair" with + // other compilers. + std::string s = name; + // Only strip the leading "struct " and "class ", so uses rfind == 0 to + // ensure that + if (s.rfind("struct ", 0) == 0) { + s = s.substr(strlen("struct ")); + } else if (s.rfind("class ", 0) == 0) { + s = s.substr(strlen("class ")); + } + return s; +#else + return name; +#endif // GTEST_HAS_CXXABI_H_ || __HP_aCC +} +#endif // GTEST_HAS_RTTI + +// GetTypeName() returns a human-readable name of type T if and only if +// RTTI is enabled, otherwise it returns a dummy type name. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template +std::string GetTypeName() { +#if GTEST_HAS_RTTI + return GetTypeName(typeid(T)); +#else + return ""; +#endif // GTEST_HAS_RTTI +} + +// A unique type indicating an empty node +struct None {}; + +#define GTEST_TEMPLATE_ \ + template \ + class + +// The template "selector" struct TemplateSel is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel::Bind::type is defined +// as the type Tmpl. This allows us to actually instantiate the +// template "selected" by TemplateSel. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template +struct TemplateSel { + template + struct Bind { + typedef Tmpl type; + }; +}; + +#define GTEST_BIND_(TmplSel, T) TmplSel::template Bind::type + +template +struct Templates { + using Head = TemplateSel; + using Tail = Templates; +}; + +template +struct Templates { + using Head = TemplateSel; + using Tail = None; +}; + +// Tuple-like type lists +template +struct Types { + using Head = Head_; + using Tail = Types; +}; + +template +struct Types { + using Head = Head_; + using Tail = None; +}; + +// Helper metafunctions to tell apart a single type from types +// generated by ::testing::Types +template +struct ProxyTypeList { + using type = Types; +}; + +template +struct is_proxy_type_list : std::false_type {}; + +template +struct is_proxy_type_list> : std::true_type {}; + +// Generator which conditionally creates type lists. +// It recognizes if a requested type list should be created +// and prevents creating a new type list nested within another one. +template +struct GenerateTypeList { + private: + using proxy = typename std::conditional::value, T, + ProxyTypeList>::type; + + public: + using type = typename proxy::type; +}; + +} // namespace internal + +template +using Types = internal::ProxyTypeList; + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ diff --git a/Tests/ThirdParty/googletest/lib/Debug/gtest.lib b/Tests/ThirdParty/googletest/lib/Debug/gtest.lib new file mode 100644 index 0000000..57a84e9 Binary files /dev/null and b/Tests/ThirdParty/googletest/lib/Debug/gtest.lib differ diff --git a/Tests/ThirdParty/googletest/lib/Debug/gtest.pdb b/Tests/ThirdParty/googletest/lib/Debug/gtest.pdb new file mode 100644 index 0000000..8b72ba7 Binary files /dev/null and b/Tests/ThirdParty/googletest/lib/Debug/gtest.pdb differ diff --git a/Tests/ThirdParty/googletest/lib/Debug/gtest_main.lib b/Tests/ThirdParty/googletest/lib/Debug/gtest_main.lib new file mode 100644 index 0000000..8b1b8e7 Binary files /dev/null and b/Tests/ThirdParty/googletest/lib/Debug/gtest_main.lib differ diff --git a/Tests/ThirdParty/googletest/lib/Debug/gtest_main.pdb b/Tests/ThirdParty/googletest/lib/Debug/gtest_main.pdb new file mode 100644 index 0000000..12f014c Binary files /dev/null and b/Tests/ThirdParty/googletest/lib/Debug/gtest_main.pdb differ diff --git a/Tests/ThirdParty/googletest/lib/Release/gtest.lib b/Tests/ThirdParty/googletest/lib/Release/gtest.lib new file mode 100644 index 0000000..211357f Binary files /dev/null and b/Tests/ThirdParty/googletest/lib/Release/gtest.lib differ diff --git a/Tests/ThirdParty/googletest/lib/Release/gtest_main.lib b/Tests/ThirdParty/googletest/lib/Release/gtest_main.lib new file mode 100644 index 0000000..cf9a598 Binary files /dev/null and b/Tests/ThirdParty/googletest/lib/Release/gtest_main.lib differ diff --git a/ThirdParty/entt/LICENSE b/ThirdParty/entt/LICENSE new file mode 100644 index 0000000..c6595f0 --- /dev/null +++ b/ThirdParty/entt/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2023 Michele Caini, author of EnTT + +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/ThirdParty/entt/entt.hpp b/ThirdParty/entt/entt.hpp new file mode 100644 index 0000000..b3a763a --- /dev/null +++ b/ThirdParty/entt/entt.hpp @@ -0,0 +1,89907 @@ +// IWYU pragma: begin_exports +// #include "config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "config/macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + +// #include "config/version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + +// #include "container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it->second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[offset].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[offset].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +#endif + +// #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 + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#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, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); + + /** + * @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) { + 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) { + constexpr auto mask = (1 << Bit) - 1; + constexpr auto buckets = 1 << Bit; + + std::size_t index[buckets]{}; + std::size_t count[buckets]{}; + + for(auto it = from; it != to; ++it) { + ++count[(getter(*it) >> start) & mask]; + } + + for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { + index[pos + 1u] = index[pos] + count[pos]; + } + + for(auto it = from; it != to; ++it) { + out[index[(getter(*it) >> start) & mask]++] = std::move(*it); + } + }; + + 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); + } + } + } +}; + +} // namespace entt + +#endif + +// #include "core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #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 "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr; + size_type length; + hash_type hash; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers 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. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{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. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> 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 +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @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 differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @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 first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @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 first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @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 first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @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 first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) 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; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, the object owns the contained element. */ + owner, + /*! @brief Aliasing mode, the object _points_ to a non-const element. */ + ref, + /*! @brief Const aliasing mode, the object _points_ to a const element. */ + cref +}; + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + using operation = internal::any_operation; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; + + template + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { + static_assert(!std::is_void_v && std::is_same_v>, Type>, "Invalid type"); + const Type *element = nullptr; + + if constexpr(in_situ) { + element = (value.mode == any_policy::owner) ? reinterpret_cast(&value.storage) : static_cast(value.instance); + } else { + element = static_cast(value.instance); + } + + switch(op) { + case operation::copy: + if constexpr(std::is_copy_constructible_v) { + static_cast(const_cast(other))->initialize(*element); + } + break; + case operation::move: + if constexpr(in_situ) { + if(value.mode == any_policy::owner) { + return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; + } + } + + return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); + case operation::transfer: + if constexpr(std::is_move_assignable_v) { + *const_cast(element) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case operation::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(element) = *static_cast(other); + return other; + } + break; + case operation::destroy: + if constexpr(in_situ) { + element->~Type(); + } else if constexpr(std::is_array_v) { + delete[] element; + } else { + delete element; + } + break; + case operation::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return *element == *static_cast(other) ? other : nullptr; + } else { + return (element == other) ? other : nullptr; + } + case operation::get: + return element; + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + + if constexpr(!std::is_void_v) { + vtable = basic_vtable>>; + + if constexpr(std::is_lvalue_reference_v) { + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + instance = (std::addressof(args), ...); + } else if constexpr(in_situ>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; + } else { + new(&storage) std::remove_cv_t>(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + instance = new std::remove_cv_t>{std::forward(args)...}; + } else { + instance = new std::remove_cv_t>(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const any_policy pol) noexcept + : instance{other.data()}, + info{other.info}, + vtable{other.vtable}, + mode{pol} {} + +public: + /*! @brief Size of the internal storage. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : instance{}, + info{}, + vtable{}, + mode{any_policy::owner} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{} { + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : instance{}, + info{other.info}, + vtable{other.vtable}, + mode{other.mode} { + if(other.vtable) { + other.vtable(operation::move, other, this); + } + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + reset(); + + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + reset(); + + if(other.vtable) { + other.vtable(operation::move, other, this); + info = other.info; + vtable = other.vtable; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the object type if any, `type_id()` otherwise. + * @return The object type if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return *info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return vtable ? vtable(operation::get, *this, nullptr) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return *info == req ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @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) { + reset(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(basic_any &&other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + if(auto *val = other.data(); val) { + return (vtable(operation::transfer, *this, val) != nullptr); + } else { + return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); + } + } + + return false; + } + + /*! @brief Destroys contained object */ + void reset() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); + info = &type_id(); + vtable = nullptr; + mode = any_policy::owner; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return vtable != nullptr; + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + return basic_any{*this, any_policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::owner); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + any_policy mode; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &data) noexcept { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } else { + return any_cast(data); + } + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + const auto &info = type_id>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#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/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "core/enum.hpp" +#ifndef ENTT_CORE_ENUM_HPP +#define ENTT_CORE_ENUM_HPP + +#include + +namespace entt { + +/** + * @brief Enable bitmask support for enum classes. + * @tparam Type The enum type for which to enable bitmask support. + */ +template +struct enum_as_bitmask: std::false_type {}; + +/*! @copydoc enum_as_bitmask */ +template +struct enum_as_bitmask>: std::is_enum {}; + +/** + * @brief Helper variable template. + * @tparam Type The enum class type for which to enable bitmask support. + */ +template +inline constexpr bool enum_as_bitmask_v = enum_as_bitmask::value; + +} // namespace entt + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param lhs The first value to use. + * @param rhs The second value to use. + * @return The result of invoking the operator on the underlying types of the + * two values provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator|(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator&(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator^(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); +} + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param value The value to use. + * @return The result of invoking the operator on the underlying types of the + * value provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator~(const Type value) noexcept { + return static_cast(~static_cast>(value)); +} + +/*! @copydoc operator~ */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> +operator!(const Type value) noexcept { + return !static_cast>(value); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator|=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator&=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator^=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs ^ rhs)); +} + +#endif + +// #include "core/family.hpp" +#ifndef ENTT_CORE_FAMILY_HPP +#define ENTT_CORE_FAMILY_HPP + +// #include "../config/config.h" + +// #include "fwd.hpp" + + +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 value_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 value_type value = identifier++; +}; + +} // namespace entt + +#endif + +// #include "core/hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr; + size_type length; + hash_type hash; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers 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. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{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. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> 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 +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @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 differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @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 first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @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 first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @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 first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @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 first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) 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; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + +// #include "core/ident.hpp" +#ifndef ENTT_CORE_IDENT_HPP +#define ENTT_CORE_IDENT_HPP + +#include +#include +#include +// #include "fwd.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @brief Type integral identifiers. + * @tparam Type List of types for which to generate identifiers. + */ +template +class ident { + template + [[nodiscard]] static constexpr id_type get(std::index_sequence) noexcept { + static_assert((std::is_same_v || ...), "Invalid type"); + return (0 + ... + (std::is_same_v...>>> ? id_type{Index} : id_type{})); + } + +public: + /*! @brief Unsigned integer type. */ + using value_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template + static constexpr value_type value = get>(std::index_sequence_for{}); +}; + +} // namespace entt + +#endif + +// #include "core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#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 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 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 = {}; + +} // namespace entt + +#endif + +// #include "core/tuple.hpp" +#ifndef ENTT_CORE_TUPLE_HPP +#define ENTT_CORE_TUPLE_HPP + +#include +#include +#include + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct is_tuple_impl: std::false_type {}; + +template +struct is_tuple_impl>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is a + * tuple, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_tuple: internal::is_tuple_impl> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_tuple_v = is_tuple::value; + +/** + * @brief Utility function to unwrap tuples of a single element. + * @tparam Type Tuple type of any sizes. + * @param value A tuple object of the given type. + * @return The tuple itself if it contains more than one element, the first + * element otherwise. + */ +template +constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept { + if constexpr(std::tuple_size_v> == 1u) { + return std::get<0>(std::forward(value)); + } else { + return std::forward(value); + } +} + +/** + * @brief Utility class to forward-and-apply tuple objects. + * @tparam Func Type of underlying invocable object. + */ +template +struct forward_apply: private Func { + /** + * @brief Constructs a forward-and-apply object. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v) + : Func{std::forward(args)...} {} + + /** + * @brief Forwards and applies the arguments with the underlying function. + * @tparam Type Tuple-like type to forward to the underlying function. + * @param args Parameters to forward to the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval(), args))) { + return std::apply(static_cast(*this), std::forward(args)); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval(), args))) { + return std::apply(static_cast(*this), std::forward(args)); + } +}; + +/** + * @brief Deduction guide. + * @tparam Func Type of underlying invocable object. + */ +template +forward_apply(Func) -> forward_apply>>; + +} // namespace entt + +#endif + +// #include "core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "entity/component.hpp" +#ifndef ENTT_ENTITY_COMPONENT_HPP +#define ENTT_ENTITY_COMPONENT_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_ENTITY_FWD_HPP +#define ENTT_ENTITY_FWD_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @brief Default entity identifier. */ +enum class entity : id_type {}; + +/*! @brief Storage deletion policy. */ +enum class deletion_policy : std::uint8_t { + /*! @brief Swap-and-pop deletion policy. */ + swap_and_pop = 0u, + /*! @brief In-place deletion policy. */ + in_place = 1u, + /*! @brief Swap-only deletion policy. */ + swap_only = 2u +}; + +template> +class basic_sparse_set; + +template, typename = void> +class basic_storage; + +template +class basic_sigh_mixin; + +template> +class basic_registry; + +template +class basic_view; + +template> +class basic_runtime_view; + +template +class basic_group; + +template> +class basic_observer; + +template +class basic_organizer; + +template +struct basic_handle; + +template +class basic_snapshot; + +template +class basic_snapshot_loader; + +template +class basic_continuous_loader; + +/*! @brief Alias declaration for the most common use case. */ +using sparse_set = basic_sparse_set<>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Type of objects assigned to the entities. + */ +template +using storage = basic_storage; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Underlying storage type. + */ +template +using sigh_mixin = basic_sigh_mixin>; + +/*! @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 organizer = basic_organizer; + +/*! @brief Alias declaration for the most common use case. */ +using handle = basic_handle; + +/*! @brief Alias declaration for the most common use case. */ +using const_handle = basic_handle; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template +using handle_view = basic_handle; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template +using const_handle_view = basic_handle; + +/*! @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. */ +using runtime_view = basic_runtime_view; + +/*! @brief Alias declaration for the most common use case. */ +using const_runtime_view = basic_runtime_view; + +/** + * @brief Alias for exclusion lists. + * @tparam Type List of types. + */ +template +struct exclude_t final: type_list { + /*! @brief Default constructor. */ + explicit constexpr exclude_t() {} +}; + +/** + * @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 final: type_list { + /*! @brief Default constructor. */ + explicit constexpr get_t() {} +}; + +/** + * @brief Variable template for lists of observed components. + * @tparam Type List of types. + */ +template +inline constexpr get_t get{}; + +/** + * @brief Alias for lists of owned components. + * @tparam Type List of types. + */ +template +struct owned_t final: type_list { + /*! @brief Default constructor. */ + explicit constexpr owned_t() {} +}; + +/** + * @brief Variable template for lists of owned components. + * @tparam Type List of types. + */ +template +inline constexpr owned_t owned{}; + +/** + * @brief Applies a given _function_ to a get list and generate a new list. + * @tparam Type Types provided by the get list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting get list after applying the transform function. */ + using type = get_t::type...>; +}; + +/** + * @brief Applies a given _function_ to an exclude list and generate a new list. + * @tparam Type Types provided by the exclude list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting exclude list after applying the transform function. */ + using type = exclude_t::type...>; +}; + +/** + * @brief Applies a given _function_ to an owned list and generate a new list. + * @tparam Type Types provided by the owned list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting owned list after applying the transform function. */ + using type = owned_t::type...>; +}; + +/** + * @brief Provides a common way to define storage types. + * @tparam Type Storage value type. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template, typename = void> +struct storage_type { + /*! @brief Type-to-storage conversion result. */ + using type = sigh_mixin>; +}; + +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template +using storage_type_t = typename storage_type::type; + +/** + * Type-to-storage conversion utility that preserves constness. + * @tparam Type Storage value type, eventually const. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template>> +struct storage_for { + /*! @brief Type-to-storage conversion result. */ + using type = constness_as_t, Entity, Allocator>, Type>; +}; + +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template +using storage_for_t = typename storage_for::type; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template> +using view = basic_view, type_list_transform_t>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +using group = basic_group, type_list_transform_t, type_list_transform_t>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; + +template<> +struct in_place_delete: std::false_type {}; + +template +struct in_place_delete> + : std::true_type {}; + +template +struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; + +template<> +struct page_size: std::integral_constant {}; + +template +struct page_size> + : std::integral_constant {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common way to access various properties of components. + * @tparam Type Type of component. + */ +template +struct component_traits { + static_assert(std::is_same_v, Type>, "Unsupported type"); + + /*! @brief Component type. */ + using type = Type; + + /*! @brief Pointer stability, default is `false`. */ + static constexpr bool in_place_delete = internal::in_place_delete::value; + /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ + static constexpr std::size_t page_size = internal::page_size::value; +}; + +} // namespace entt + +#endif + +// #include "entity/entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +// waiting for C++20 and std::popcount +template +constexpr int popcount(Type value) noexcept { + return value ? (int(value & 1) + popcount(value >> 1)) : 0; +} + +template +struct entt_traits; + +template +struct entt_traits>> + : entt_traits> { + using value_type = Type; +}; + +template +struct entt_traits>> + : entt_traits { + using value_type = Type; +}; + +template<> +struct entt_traits { + using value_type = std::uint32_t; + + using entity_type = std::uint32_t; + using version_type = std::uint16_t; + + static constexpr entity_type entity_mask = 0xFFFFF; + static constexpr entity_type version_mask = 0xFFF; +}; + +template<> +struct entt_traits { + using value_type = std::uint64_t; + + using entity_type = std::uint64_t; + using version_type = std::uint32_t; + + static constexpr entity_type entity_mask = 0xFFFFFFFF; + static constexpr entity_type version_mask = 0xFFFFFFFF; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common basic entity traits implementation. + * @tparam Traits Actual entity traits to use. + */ +template +class basic_entt_traits { + static constexpr auto length = internal::popcount(Traits::entity_mask); + + static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask"); + static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask"); + +public: + /*! @brief Value type. */ + using value_type = typename Traits::value_type; + /*! @brief Underlying entity type. */ + using entity_type = typename Traits::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename Traits::version_type; + + /*! @brief Entity mask size. */ + static constexpr entity_type entity_mask = Traits::entity_mask; + /*! @brief Version mask size */ + static constexpr entity_type version_mask = Traits::version_mask; + + /** + * @brief Converts an entity to its underlying type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ + [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { + return static_cast(value); + } + + /** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { + return (to_integral(value) & entity_mask); + } + + /** + * @brief Returns the version part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ + [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { + return (static_cast(to_integral(value) >> length) & version_mask); + } + + /** + * @brief Returns the successor of a given identifier. + * @param value The identifier of which to return the successor. + * @return The successor of the given identifier. + */ + [[nodiscard]] static constexpr value_type next(const value_type value) noexcept { + const auto vers = to_version(value) + 1; + return construct(to_integral(value), static_cast(vers + (vers == version_mask))); + } + + /** + * @brief Constructs an identifier from its parts. + * + * If the version part is not provided, a tombstone is returned.
+ * If the entity part is not provided, a null identifier is returned. + * + * @param entity The entity part of the identifier. + * @param version The version part of the identifier. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { + return value_type{(entity & entity_mask) | (static_cast(version & version_mask) << length)}; + } + + /** + * @brief Combines two identifiers in a single one. + * + * The returned identifier is a copy of the first element except for its + * version, which is taken from the second element. + * + * @param lhs The identifier from which to take the entity part. + * @param rhs The identifier from which to take the version part. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { + return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))}; + } +}; + +/** + * @brief Entity traits. + * @tparam Type Type of identifier. + */ +template +struct entt_traits: basic_entt_traits> { + /*! @brief Base type. */ + using base_type = basic_entt_traits>; + /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ + static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; +}; + +/** + * @brief Converts an entity to its underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { + return entt_traits::to_integral(value); +} + +/** + * @brief Returns the entity part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { + return entt_traits::to_entity(value); +} + +/** + * @brief Returns the version part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ +template +[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { + return entt_traits::to_version(value); +} + +/*! @brief Null object for all identifiers. */ +struct null_t { + /** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The null representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { + return true; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { + return false; + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_entity(entity) == traits_type::to_entity(*this); + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept { + return other.operator==(entity); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept { + return !(other == entity); +} + +/*! @brief Tombstone object for all identifiers. */ +struct tombstone_t { + /** + * @brief Converts the tombstone object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The tombstone representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { + return true; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { + return false; + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_version(entity) == traits_type::to_version(*this); + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept { + return other.operator==(entity); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept { + return !(other == entity); +} + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the null + * entity and any other identifier. + */ +inline constexpr null_t null{}; + +/** + * @brief Compile-time constant for tombstone entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the + * tombstone entity and any other identifier. + */ +inline constexpr tombstone_t tombstone{}; + +} // namespace entt + +#endif + +// #include "entity/group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #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 "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr; + size_type length; + hash_type hash; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers 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. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{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. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> 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 +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @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 differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @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 first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @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 first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @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 first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @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 first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) 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; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +// waiting for C++20 and std::popcount +template +constexpr int popcount(Type value) noexcept { + return value ? (int(value & 1) + popcount(value >> 1)) : 0; +} + +template +struct entt_traits; + +template +struct entt_traits>> + : entt_traits> { + using value_type = Type; +}; + +template +struct entt_traits>> + : entt_traits { + using value_type = Type; +}; + +template<> +struct entt_traits { + using value_type = std::uint32_t; + + using entity_type = std::uint32_t; + using version_type = std::uint16_t; + + static constexpr entity_type entity_mask = 0xFFFFF; + static constexpr entity_type version_mask = 0xFFF; +}; + +template<> +struct entt_traits { + using value_type = std::uint64_t; + + using entity_type = std::uint64_t; + using version_type = std::uint32_t; + + static constexpr entity_type entity_mask = 0xFFFFFFFF; + static constexpr entity_type version_mask = 0xFFFFFFFF; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common basic entity traits implementation. + * @tparam Traits Actual entity traits to use. + */ +template +class basic_entt_traits { + static constexpr auto length = internal::popcount(Traits::entity_mask); + + static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask"); + static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask"); + +public: + /*! @brief Value type. */ + using value_type = typename Traits::value_type; + /*! @brief Underlying entity type. */ + using entity_type = typename Traits::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename Traits::version_type; + + /*! @brief Entity mask size. */ + static constexpr entity_type entity_mask = Traits::entity_mask; + /*! @brief Version mask size */ + static constexpr entity_type version_mask = Traits::version_mask; + + /** + * @brief Converts an entity to its underlying type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ + [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { + return static_cast(value); + } + + /** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { + return (to_integral(value) & entity_mask); + } + + /** + * @brief Returns the version part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ + [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { + return (static_cast(to_integral(value) >> length) & version_mask); + } + + /** + * @brief Returns the successor of a given identifier. + * @param value The identifier of which to return the successor. + * @return The successor of the given identifier. + */ + [[nodiscard]] static constexpr value_type next(const value_type value) noexcept { + const auto vers = to_version(value) + 1; + return construct(to_integral(value), static_cast(vers + (vers == version_mask))); + } + + /** + * @brief Constructs an identifier from its parts. + * + * If the version part is not provided, a tombstone is returned.
+ * If the entity part is not provided, a null identifier is returned. + * + * @param entity The entity part of the identifier. + * @param version The version part of the identifier. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { + return value_type{(entity & entity_mask) | (static_cast(version & version_mask) << length)}; + } + + /** + * @brief Combines two identifiers in a single one. + * + * The returned identifier is a copy of the first element except for its + * version, which is taken from the second element. + * + * @param lhs The identifier from which to take the entity part. + * @param rhs The identifier from which to take the version part. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { + return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))}; + } +}; + +/** + * @brief Entity traits. + * @tparam Type Type of identifier. + */ +template +struct entt_traits: basic_entt_traits> { + /*! @brief Base type. */ + using base_type = basic_entt_traits>; + /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ + static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; +}; + +/** + * @brief Converts an entity to its underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { + return entt_traits::to_integral(value); +} + +/** + * @brief Returns the entity part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { + return entt_traits::to_entity(value); +} + +/** + * @brief Returns the version part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ +template +[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { + return entt_traits::to_version(value); +} + +/*! @brief Null object for all identifiers. */ +struct null_t { + /** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The null representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { + return true; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { + return false; + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_entity(entity) == traits_type::to_entity(*this); + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept { + return other.operator==(entity); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept { + return !(other == entity); +} + +/*! @brief Tombstone object for all identifiers. */ +struct tombstone_t { + /** + * @brief Converts the tombstone object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The tombstone representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { + return true; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { + return false; + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_version(entity) == traits_type::to_version(*this); + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept { + return other.operator==(entity); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept { + return !(other == entity); +} + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the null + * entity and any other identifier. + */ +inline constexpr null_t null{}; + +/** + * @brief Compile-time constant for tombstone entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the + * tombstone entity and any other identifier. + */ +inline constexpr tombstone_t tombstone{}; + +} // namespace entt + +#endif + +// #include "fwd.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" +#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 + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#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, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); + + /** + * @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) { + 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) { + constexpr auto mask = (1 << Bit) - 1; + constexpr auto buckets = 1 << Bit; + + std::size_t index[buckets]{}; + std::size_t count[buckets]{}; + + for(auto it = from; it != to; ++it) { + ++count[(getter(*it) >> start) & mask]; + } + + for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { + index[pos + 1u] = index[pos] + count[pos]; + } + + for(auto it = from; it != to; ++it) { + out[index[(getter(*it) >> start) & mask]++] = std::move(*it); + } + }; + + 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); + } + } + } +}; + +} // namespace entt + +#endif + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, the object owns the contained element. */ + owner, + /*! @brief Aliasing mode, the object _points_ to a non-const element. */ + ref, + /*! @brief Const aliasing mode, the object _points_ to a const element. */ + cref +}; + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + using operation = internal::any_operation; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; + + template + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { + static_assert(!std::is_void_v && std::is_same_v>, Type>, "Invalid type"); + const Type *element = nullptr; + + if constexpr(in_situ) { + element = (value.mode == any_policy::owner) ? reinterpret_cast(&value.storage) : static_cast(value.instance); + } else { + element = static_cast(value.instance); + } + + switch(op) { + case operation::copy: + if constexpr(std::is_copy_constructible_v) { + static_cast(const_cast(other))->initialize(*element); + } + break; + case operation::move: + if constexpr(in_situ) { + if(value.mode == any_policy::owner) { + return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; + } + } + + return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); + case operation::transfer: + if constexpr(std::is_move_assignable_v) { + *const_cast(element) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case operation::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(element) = *static_cast(other); + return other; + } + break; + case operation::destroy: + if constexpr(in_situ) { + element->~Type(); + } else if constexpr(std::is_array_v) { + delete[] element; + } else { + delete element; + } + break; + case operation::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return *element == *static_cast(other) ? other : nullptr; + } else { + return (element == other) ? other : nullptr; + } + case operation::get: + return element; + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + + if constexpr(!std::is_void_v) { + vtable = basic_vtable>>; + + if constexpr(std::is_lvalue_reference_v) { + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + instance = (std::addressof(args), ...); + } else if constexpr(in_situ>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; + } else { + new(&storage) std::remove_cv_t>(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + instance = new std::remove_cv_t>{std::forward(args)...}; + } else { + instance = new std::remove_cv_t>(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const any_policy pol) noexcept + : instance{other.data()}, + info{other.info}, + vtable{other.vtable}, + mode{pol} {} + +public: + /*! @brief Size of the internal storage. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : instance{}, + info{}, + vtable{}, + mode{any_policy::owner} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{} { + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : instance{}, + info{other.info}, + vtable{other.vtable}, + mode{other.mode} { + if(other.vtable) { + other.vtable(operation::move, other, this); + } + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + reset(); + + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + reset(); + + if(other.vtable) { + other.vtable(operation::move, other, this); + info = other.info; + vtable = other.vtable; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the object type if any, `type_id()` otherwise. + * @return The object type if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return *info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return vtable ? vtable(operation::get, *this, nullptr) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return *info == req ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @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) { + reset(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(basic_any &&other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + if(auto *val = other.data(); val) { + return (vtable(operation::transfer, *this, val) != nullptr); + } else { + return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); + } + } + + return false; + } + + /*! @brief Destroys contained object */ + void reset() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); + info = &type_id(); + vtable = nullptr; + mode = any_policy::owner; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return vtable != nullptr; + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + return basic_any{*this, any_policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::owner); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + any_policy mode; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &data) noexcept { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } else { + return any_cast(data); + } + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + const auto &info = type_id>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct sparse_set_iterator final { + using value_type = typename Container::value_type; + using pointer = typename Container::const_pointer; + using reference = typename Container::const_reference; + using difference_type = typename Container::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr sparse_set_iterator() noexcept + : packed{}, + offset{} {} + + constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept + : packed{std::addressof(ref)}, + offset{idx} {} + + constexpr sparse_set_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr sparse_set_iterator operator++(int) noexcept { + sparse_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr sparse_set_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr sparse_set_iterator operator--(int) noexcept { + sparse_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr sparse_set_iterator operator+(const difference_type value) const noexcept { + sparse_set_iterator copy = *this; + return (copy += value); + } + + constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr sparse_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return packed->data()[index() - value]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return packed->data() + index(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer data() const noexcept { + return packed ? packed->data() : nullptr; + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + const Container *packed; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 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 + * Internal data structures arrange elements to maximize performance. 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. + * + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector; + using underlying_type = typename entt_traits::entity_type; + + [[nodiscard]] auto sparse_ptr(const Entity entt) const { + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr; + } + + [[nodiscard]] auto &sparse_ref(const Entity entt) const { + ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); + const auto pos = static_cast(traits_type::to_entity(entt)); + return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + [[nodiscard]] auto to_iterator(const Entity entt) const { + return --(end() - index(entt)); + } + + [[nodiscard]] auto &assure_at_least(const Entity entt) { + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + + if(!(page < sparse.size())) { + sparse.resize(page + 1u, nullptr); + } + + if(!sparse[page]) { + constexpr entity_type init = null; + auto page_allocator{packed.get_allocator()}; + sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size); + std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init); + } + + return sparse[page][fast_mod(pos, traits_type::page_size)]; + } + + void release_sparse_pages() { + auto page_allocator{packed.get_allocator()}; + + for(auto &&page: sparse) { + if(page != nullptr) { + std::destroy(page, page + traits_type::page_size); + alloc_traits::deallocate(page_allocator, page, traits_type::page_size); + page = nullptr; + } + } + } + + void swap_at(const std::size_t from, const std::size_t to) { + auto &lhs = packed[from]; + auto &rhs = packed[to]; + + sparse_ref(lhs) = traits_type::combine(static_cast(to), traits_type::to_integral(lhs)); + sparse_ref(rhs) = traits_type::combine(static_cast(from), traits_type::to_integral(rhs)); + + std::swap(lhs, rhs); + } + + underlying_type policy_to_head() { + return traits_type::entity_mask * (mode != deletion_policy::swap_only); + } + +private: + virtual const void *get_at(const std::size_t) const { + return nullptr; + } + + virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) { + ENTT_ASSERT((mode != deletion_policy::swap_only) || (((lhs < free_list()) + (rhs < free_list())) != 1u), "Cross swapping is not supported"); + } + +protected: + /*! @brief Random access iterator type. */ + using basic_iterator = internal::sparse_set_iterator; + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_only(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch"); + const auto pos = static_cast(index(*it)); + bump(traits_type::next(*it)); + swap_at(pos, static_cast(head -= (pos < head))); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_and_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch"); + auto &self = sparse_ref(*it); + const auto entt = traits_type::to_entity(self); + sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back())); + packed[static_cast(entt)] = packed.back(); + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((packed.back() = null, true), ""); + // lazy self-assignment guard + self = null; + packed.pop_back(); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void in_place_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch"); + const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null)); + packed[static_cast(entt)] = traits_type::combine(std::exchange(head, entt), tombstone); + } + +protected: + /** + * @brief Erases entities from a sparse set. + * @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. + */ + virtual void pop(basic_iterator first, basic_iterator last) { + switch(mode) { + case deletion_policy::swap_and_pop: + for(; first != last; ++first) { + swap_and_pop(first); + } + break; + case deletion_policy::in_place: + for(; first != last; ++first) { + in_place_pop(first); + } + break; + case deletion_policy::swap_only: + for(; first != last; ++first) { + swap_only(first); + } + break; + } + } + + /*! @brief Erases all entities of a sparse set. */ + virtual void pop_all() { + switch(mode) { + case deletion_policy::in_place: + if(head != traits_type::to_entity(null)) { + for(auto first = begin(); !(first.index() < 0); ++first) { + if(*first != tombstone) { + sparse_ref(*first) = null; + } + } + break; + } + [[fallthrough]]; + case deletion_policy::swap_only: + case deletion_policy::swap_and_pop: + for(auto first = begin(); !(first.index() < 0); ++first) { + sparse_ref(*first) = null; + } + break; + } + + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Assigns an entity to a sparse set. + * @param entt A valid identifier. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ + virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) { + auto &elem = assure_at_least(entt); + auto pos = size(); + + switch(mode) { + case deletion_policy::in_place: + if(head != traits_type::to_entity(null) && !force_back) { + pos = static_cast(head); + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(head, traits_type::to_integral(entt)); + head = traits_type::to_entity(std::exchange(packed[pos], entt)); + break; + } + [[fallthrough]]; + case deletion_policy::swap_and_pop: + packed.push_back(entt); + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + break; + case deletion_policy::swap_only: + if(elem == null) { + packed.push_back(entt); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + } else { + ENTT_ASSERT(!(traits_type::to_entity(elem) < head), "Slot not available"); + bump(entt); + } + + if(force_back) { + pos = static_cast(head++); + swap_at(static_cast(traits_type::to_entity(elem)), pos); + } + + break; + } + + return --(end() - pos); + } + +public: + /*! @brief Entity traits. */ + using traits_type = entt_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type to contained entities. */ + using pointer = typename packed_container_type::const_pointer; + /*! @brief Random access iterator type. */ + using iterator = basic_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + + /*! @brief Default constructor. */ + basic_sparse_set() + : basic_sparse_set{type_id()} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sparse_set(const allocator_type &allocator) + : basic_sparse_set{type_id(), deletion_policy::swap_and_pop, allocator} {} + + /** + * @brief Constructs an empty container with the given policy and allocator. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ + explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {}) + : basic_sparse_set{type_id(), pol, allocator} {} + + /** + * @brief Constructs an empty container with the given value type, policy + * and allocator. + * @param elem Returned value type, if any. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ + explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) + : sparse{allocator}, + packed{allocator}, + info{&elem}, + mode{pol}, + head{policy_to_head()} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sparse_set(basic_sparse_set &&other) noexcept + : sparse{std::move(other.sparse)}, + packed{std::move(other.packed)}, + info{other.info}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept + : sparse{std::move(other.sparse), allocator}, + packed{std::move(other.packed), allocator}, + info{other.info}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); + } + + /*! @brief Default destructor. */ + virtual ~basic_sparse_set() { + release_sparse_pages(); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This sparse set. + */ + basic_sparse_set &operator=(basic_sparse_set &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); + + release_sparse_pages(); + sparse = std::move(other.sparse); + packed = std::move(other.packed); + info = other.info; + mode = other.mode; + head = std::exchange(other.head, policy_to_head()); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given sparse set. + * @param other Sparse set to exchange the content with. + */ + void swap(basic_sparse_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(info, other.info); + swap(mode, other.mode); + swap(head, other.head); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return packed.get_allocator(); + } + + /** + * @brief Returns the deletion policy of a sparse set. + * @return The deletion policy of the sparse set. + */ + [[nodiscard]] deletion_policy policy() const noexcept { + return mode; + } + + /** + * @brief Returns the head of the free list, if any. + * @return The head of the free list. + */ + [[nodiscard]] size_type free_list() const noexcept { + return static_cast(head); + } + + /** + * @brief Sets the head of the free list, if possible. + * @param len The value to use as the new head of the free list. + */ + void free_list(const size_type len) noexcept { + ENTT_ASSERT((mode == deletion_policy::swap_only) && !(len > packed.size()), "Invalid value"); + head = static_cast(len); + } + + /** + * @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. + */ + virtual 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. + */ + [[nodiscard]] virtual size_type capacity() const noexcept { + return packed.capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + virtual void 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. + */ + [[nodiscard]] size_type extent() const noexcept { + return sparse.size() * traits_type::page_size; + } + + /** + * @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. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.size(); + } + + /** + * @brief Checks whether a sparse set is empty. + * @return True if the sparse set is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.empty(); + } + + /** + * @brief Checks whether a sparse set is fully packed. + * @return True if the sparse set is fully packed, false otherwise. + */ + [[nodiscard]] bool contiguous() const noexcept { + return (mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)); + } + + /** + * @brief Direct access to the internal packed array. + * @return A pointer to the internal packed array. + */ + [[nodiscard]] pointer data() const noexcept { + return packed.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the sparse set is empty, the returned iterator will be equal to + * `end()`. + * + * @return An iterator to the first entity of the sparse set. + */ + [[nodiscard]] iterator begin() const noexcept { + const auto pos = static_cast(packed.size()); + return iterator{packed, pos}; + } + + /*! @copydoc begin */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last entity of a sparse + * set. + */ + [[nodiscard]] iterator end() const noexcept { + return iterator{packed, {}}; + } + + /*! @copydoc end */ + [[nodiscard]] const_iterator cend() const noexcept { + return end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the sparse set is empty, the returned iterator will be equal to + * `rend()`. + * + * @return An iterator to the first entity of the reversed internal packed + * array. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return std::make_reverse_iterator(end()); + } + + /*! @copydoc rbegin */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last entity of the + * reversed sparse set. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return std::make_reverse_iterator(begin()); + } + + /*! @copydoc rend */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return rend(); + } + + /*! @copydoc begin Useful only in case of swap-only policy. */ + [[nodiscard]] iterator begin(int) const noexcept { + return (mode == deletion_policy::swap_only) ? (end() - static_cast(head)) : begin(); + } + + /*! @copydoc cbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_iterator cbegin(int) const noexcept { + return begin(0); + } + + /*! @copydoc end Useful only in case of swap-only policy. */ + [[nodiscard]] iterator end(int) const noexcept { + return end(); + } + + /*! @copydoc cend Useful only in case of swap-only policy. */ + [[nodiscard]] const_iterator cend(int) const noexcept { + return end(0); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] reverse_iterator rbegin(int) const noexcept { + return std::make_reverse_iterator(end(0)); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_reverse_iterator crbegin(int) const noexcept { + return rbegin(0); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] reverse_iterator rend(int) const noexcept { + return std::make_reverse_iterator(begin(0)); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_reverse_iterator crend(int) const noexcept { + return rend(0); + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] const_iterator find(const entity_type entt) const noexcept { + return contains(entt) ? to_iterator(entt) : end(); + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + const auto elem = sparse_ptr(entt); + constexpr auto cap = traits_type::entity_mask; + constexpr auto mask = traits_type::to_integral(null) & ~cap; + // testing versions permits to avoid accessing the packed array + return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap); + } + + /** + * @brief Returns the contained version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if present, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entt) const noexcept { + const auto elem = sparse_ptr(entt); + constexpr auto fallback = traits_type::to_version(tombstone); + return elem ? traits_type::to_version(*elem) : fallback; + } + + /** + * @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. + * + * @param entt A valid identifier. + * @return The position of the entity in the sparse set. + */ + [[nodiscard]] size_type index(const entity_type entt) const noexcept { + ENTT_ASSERT(contains(entt), "Set does not contain entity"); + return static_cast(traits_type::to_entity(sparse_ref(entt))); + } + + /** + * @brief Returns the entity at specified location, with bounds checking. + * @param pos The position for which to return the entity. + * @return The entity at specified location if any, a null entity otherwise. + */ + [[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type at(const size_type pos) const noexcept { + return pos < packed.size() ? packed[pos] : null; + } + + /** + * @brief Returns the entity at specified location, without bounds checking. + * @param pos The position for which to return the entity. + * @return The entity at specified location. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const noexcept { + ENTT_ASSERT(pos < packed.size(), "Position is out of bounds"); + return packed[pos]; + } + + /** + * @brief Returns the element assigned to an entity, if any. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior. + * + * @param entt A valid identifier. + * @return An opaque pointer to the element assigned to the entity, if any. + */ + [[nodiscard]] const void *value(const entity_type entt) const noexcept { + return get_at(index(entt)); + } + + /*! @copydoc value */ + [[nodiscard]] void *value(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).value(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. + * + * @param entt A valid identifier. + * @param elem Optional opaque element to forward to mixins, if any. + * @return Iterator pointing to the emplaced element in case of success, the + * `end()` iterator otherwise. + */ + iterator push(const entity_type entt, const void *elem = nullptr) { + return try_emplace(entt, (mode == deletion_policy::swap_only), elem); + } + + /** + * @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. + * + * @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. + * @return Iterator pointing to the first element inserted in case of + * success, the `end()` iterator otherwise. + */ + template + iterator push(It first, It last) { + for(auto it = first; it != last; ++it) { + try_emplace(*it, true); + } + + return first == last ? end() : find(*first); + } + + /** + * @brief Bump the version number of an entity. + * + * @warning + * Attempting to bump the version of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The version of the given identifier. + */ + version_type bump(const entity_type entt) { + auto &entity = sparse_ref(entt); + ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version"); + entity = traits_type::combine(traits_type::to_integral(entity), traits_type::to_integral(entt)); + packed[static_cast(traits_type::to_entity(entity))] = entt; + return traits_type::to_version(entt); + } + + /** + * @brief Erases an entity from a sparse set. + * + * @warning + * Attempting to erase an entity that doesn't belong to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + */ + void erase(const entity_type entt) { + const auto it = to_iterator(entt); + pop(it, it + 1u); + } + + /** + * @brief Erases entities from a set. + * + * @sa erase + * + * @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 erase(It first, It last) { + if constexpr(std::is_same_v) { + pop(first, last); + } else { + for(; first != last; ++first) { + erase(*first); + } + } + } + + /** + * @brief Removes an entity from a sparse set if it exists. + * @param entt A valid identifier. + * @return True if the entity is actually removed, false otherwise. + */ + bool remove(const entity_type entt) { + return contains(entt) && (erase(entt), true); + } + + /** + * @brief Removes entities from a sparse set if they exist. + * @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. + * @return The number of entities actually removed. + */ + template + size_type remove(It first, It last) { + size_type count{}; + + if constexpr(std::is_same_v) { + while(first != last) { + while(first != last && !contains(*first)) { + ++first; + } + + const auto it = first; + + while(first != last && contains(*first)) { + ++first; + } + + count += std::distance(it, first); + erase(it, first); + } + } else { + for(; first != last; ++first) { + count += remove(*first); + } + } + + return count; + } + + /*! @brief Removes all tombstones from a sparse set. */ + void compact() { + if(mode == deletion_policy::in_place) { + size_type from = packed.size(); + for(; from && packed[from - 1u] == tombstone; --from) {} + underlying_type pos = std::exchange(head, traits_type::entity_mask); + + while(pos != traits_type::to_entity(null)) { + if(const auto to = static_cast(std::exchange(pos, traits_type::to_entity(packed[pos]))); to < from) { + --from; + swap_or_move(from, to); + + packed[to] = packed[from]; + const auto entity = static_cast(to); + sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to])); + + for(; from && packed[from - 1u] == tombstone; --from) {} + } + } + + packed.erase(packed.begin() + from, packed.end()); + } + } + + /** + * @brief Swaps two entities in a sparse set. + * + * 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. + * + * @param lhs A valid identifier. + * @param rhs A valid identifier. + */ + void swap_elements(const entity_type lhs, const entity_type rhs) { + const auto from = index(lhs); + const auto to = index(rhs); + + // basic no-leak guarantee if swapping throws + swap_or_move(from, to); + swap_at(from, to); + } + + /** + * @brief Sort the first count elements according to the given comparison + * function. + * + * 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 object 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. + * + * @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 length Number of elements 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_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed"); + ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements"); + + algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward(args)...); + + for(size_type pos{}; pos < length; ++pos) { + auto curr = pos; + auto next = index(packed[curr]); + + while(curr != next) { + const auto idx = index(packed[next]); + const auto entt = packed[curr]; + + swap_or_move(next, idx); + const auto entity = static_cast(curr); + sparse_ref(entt) = traits_type::combine(entity, traits_type::to_integral(packed[curr])); + curr = std::exchange(next, idx); + } + } + } + + /** + * @brief Sort all elements according to the given comparison function. + * + * @sa sort_n + * + * @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) { + sort_n(static_cast(end(0) - begin(0)), std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort entities according to their order in a range. + * + * Entities that are part of both the sparse set and the range are ordered + * internally according to the order they have in the range.
+ * All other entities goes to the end of the sparse set and there are no + * guarantees on their order. + * + * @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 sort_as(It first, It last) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed"); + + for(auto it = begin(0); it.index() && first != last; ++first) { + if(const auto curr = *first; contains(curr)) { + if(const auto entt = *it; entt != curr) { + // basic no-leak guarantee (with invalid state) if swapping throws + swap_elements(entt, curr); + } + + ++it; + } + } + } + + /** + * @copybrief sort_as + * @param other The sparse sets that imposes the order of the entities. + */ + [[deprecated("use iterator based sort_as instead")]] void sort_as(const basic_sparse_set &other) { + sort_as(other.begin(), other.end()); + } + + /*! @brief Clears a sparse set. */ + void clear() { + pop_all(); + // sanity check to avoid subtle issues due to storage classes + ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set"); + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Returned value type, if any. + * @return Returned value type, if any. + */ + const type_info &type() const noexcept { + return *info; + } + + /*! @brief Forwards variables to derived classes, if any. */ + virtual void bind(any) noexcept {} + +private: + sparse_container_type sparse; + packed_container_type packed; + const type_info *info; + deletion_policy mode; + underlying_type head; +}; + +} // namespace entt + +#endif + +// #include "storage.hpp" +#ifndef ENTT_ENTITY_STORAGE_HPP +#define ENTT_ENTITY_STORAGE_HPP + +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "component.hpp" +#ifndef ENTT_ENTITY_COMPONENT_HPP +#define ENTT_ENTITY_COMPONENT_HPP + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; + +template<> +struct in_place_delete: std::false_type {}; + +template +struct in_place_delete> + : std::true_type {}; + +template +struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; + +template<> +struct page_size: std::integral_constant {}; + +template +struct page_size> + : std::integral_constant {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common way to access various properties of components. + * @tparam Type Type of component. + */ +template +struct component_traits { + static_assert(std::is_same_v, Type>, "Unsupported type"); + + /*! @brief Component type. */ + using type = Type; + + /*! @brief Pointer stability, default is `false`. */ + static constexpr bool in_place_delete = internal::in_place_delete::value; + /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ + static constexpr std::size_t page_size = internal::page_size::value; +}; + +} // namespace entt + +#endif + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class storage_iterator final { + friend storage_iterator; + + using container_type = std::remove_const_t; + using alloc_traits = std::allocator_traits; + + using iterator_traits = std::iterator_traits, + typename alloc_traits::template rebind_traits::element_type>::const_pointer, + typename alloc_traits::template rebind_traits::element_type>::pointer>>; + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr storage_iterator() noexcept = default; + + constexpr storage_iterator(Container *ref, const difference_type idx) noexcept + : payload{ref}, + offset{idx} {} + + template, typename = std::enable_if_t> + constexpr storage_iterator(const storage_iterator, Size> &other) noexcept + : storage_iterator{other.payload, other.offset} {} + + constexpr storage_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr storage_iterator operator++(int) noexcept { + storage_iterator orig = *this; + return ++(*this), orig; + } + + constexpr storage_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr storage_iterator operator--(int) noexcept { + storage_iterator orig = *this; + return operator--(), orig; + } + + constexpr storage_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr storage_iterator operator+(const difference_type value) const noexcept { + storage_iterator copy = *this; + return (copy += value); + } + + constexpr storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + const auto pos = index() - value; + return (*payload)[pos / Size][fast_mod(pos, Size)]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + const auto pos = index(); + return (*payload)[pos / Size] + fast_mod(pos, Size); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + Container *payload; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class extended_storage_iterator final { + template + friend class extended_storage_iterator; + +public: + using iterator_type = It; + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::forward_as_tuple(*std::declval()...))); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_storage_iterator() + : it{} {} + + constexpr extended_storage_iterator(iterator_type base, Other... other) + : it{base, other...} {} + + template && ...) && (std::is_constructible_v && ...)>> + constexpr extended_storage_iterator(const extended_storage_iterator &other) + : it{other.it} {} + + constexpr extended_storage_iterator &operator++() noexcept { + return ++std::get(it), (++std::get(it), ...), *this; + } + + constexpr extended_storage_iterator operator++(int) noexcept { + extended_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {*std::get(it), *std::get(it)...}; + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return std::get(it); + } + + template + friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; + +private: + std::tuple it; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic storage implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @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. + * + * @tparam Type Type of objects assigned to the entities. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage: public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + + [[nodiscard]] auto &element_at(const std::size_t pos) const { + return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + auto assure_at_least(const std::size_t pos) { + const auto idx = pos / traits_type::page_size; + + if(!(idx < payload.size())) { + auto curr = payload.size(); + allocator_type allocator{get_allocator()}; + payload.resize(idx + 1u, nullptr); + + ENTT_TRY { + for(const auto last = payload.size(); curr < last; ++curr) { + payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size); + } + } + ENTT_CATCH { + payload.resize(curr); + ENTT_THROW; + } + } + + return payload[idx] + fast_mod(pos, traits_type::page_size); + } + + template + auto emplace_element(const Entity entt, const bool force_back, Args &&...args) { + const auto it = base_type::try_emplace(entt, force_back); + + ENTT_TRY { + auto elem = assure_at_least(static_cast(it.index())); + entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward(args)...); + } + ENTT_CATCH { + base_type::pop(it, it + 1u); + ENTT_THROW; + } + + return it; + } + + void shrink_to_size(const std::size_t sz) { + const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size; + allocator_type allocator{get_allocator()}; + + for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { + if constexpr(traits_type::in_place_delete) { + if(base_type::data()[pos] != tombstone) { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } else { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } + + for(auto pos = from, last = payload.size(); pos < last; ++pos) { + alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size); + } + + payload.resize(from); + } + +private: + const void *get_at(const std::size_t pos) const final { + return std::addressof(element_at(pos)); + } + + void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override { + static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v && std::is_move_assignable_v); + // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy + ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type"); + + if constexpr(!is_pinned_type_v) { + auto &elem = element_at(from); + + if constexpr(traits_type::in_place_delete) { + if(base_type::operator[](to) == tombstone) { + allocator_type allocator{get_allocator()}; + entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), allocator, std::move(elem)); + alloc_traits::destroy(allocator, std::addressof(elem)); + return; + } + } + + using std::swap; + swap(elem, element_at(to)); + } + } + +protected: + /** + * @brief Erases entities from a storage. + * @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. + */ + void pop(underlying_iterator first, underlying_iterator last) override { + for(allocator_type allocator{get_allocator()}; first != last; ++first) { + // cannot use first.index() because it would break with cross iterators + auto &elem = element_at(base_type::index(*first)); + + if constexpr(traits_type::in_place_delete) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(elem)); + } else { + auto &other = element_at(base_type::size() - 1u); + // destroying on exit allows reentrant destructors + [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); + alloc_traits::destroy(allocator, std::addressof(other)); + base_type::swap_and_pop(first); + } + } + } + + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + allocator_type allocator{get_allocator()}; + + for(auto first = base_type::begin(); !(first.index() < 0); ++first) { + if constexpr(traits_type::in_place_delete) { + if(*first != tombstone) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } else { + base_type::swap_and_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } + } + + /** + * @brief Assigns an entity to a storage. + * @param entt A valid identifier. + * @param value Optional opaque value. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { + if(value) { + if constexpr(std::is_copy_constructible_v) { + return emplace_element(entt, force_back, *static_cast(value)); + } else { + return base_type::end(); + } + } else { + if constexpr(std::is_default_constructible_v) { + return emplace_element(entt, force_back); + } else { + return base_type::end(); + } + } + } + +public: + /*! @brief Base type. */ + using base_type = underlying_type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Component traits. */ + using traits_type = component_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type to contained elements. */ + using pointer = typename container_type::pointer; + /*! @brief Constant pointer type to contained elements. */ + using const_pointer = typename alloc_traits::template rebind_traits::const_pointer; + /*! @brief Random access iterator type. */ + using iterator = internal::storage_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::storage_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), deletion_policy{traits_type::in_place_delete}, allocator}, + payload{allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)}, + payload{std::move(other.payload)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator}, + payload{std::move(other.payload), allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed"); + } + + /*! @brief Default destructor. */ + ~basic_storage() override { + shrink_to_size(0u); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed"); + + shrink_to_size(0u); + base_type::operator=(std::move(other)); + payload = std::move(other.payload); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_storage &other) { + using std::swap; + base_type::swap(other); + swap(payload, other.payload); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return payload.get_allocator(); + } + + /** + * @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) override { + if(cap != 0u) { + base_type::reserve(cap); + assure_at_least(cap - 1u); + } + } + + /** + * @brief Returns the number of elements that a storage has currently + * allocated space for. + * @return Capacity of the storage. + */ + [[nodiscard]] size_type capacity() const noexcept override { + return payload.size() * traits_type::page_size; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() override { + base_type::shrink_to_fit(); + shrink_to_size(base_type::size()); + } + + /** + * @brief Direct access to the array of objects. + * @return A pointer to the array of objects. + */ + [[nodiscard]] const_pointer raw() const noexcept { + return payload.data(); + } + + /*! @copydoc raw */ + [[nodiscard]] pointer raw() noexcept { + return payload.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + const auto pos = static_cast(base_type::size()); + return const_iterator{&payload, pos}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + const auto pos = static_cast(base_type::size()); + return iterator{&payload, pos}; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return const_iterator{&payload, {}}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return iterator{&payload, {}}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ + [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { + return element_at(base_type::index(entt)); + } + + /*! @copydoc get */ + [[nodiscard]] value_type &get(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).get(entt)); + } + + /** + * @brief Returns the object assigned to an entity as a tuple. + * @param entt A valid identifier. + * @return The object assigned to the entity as a tuple. + */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { + return std::forward_as_tuple(get(entt)); + } + + /*! @copydoc get_as_tuple */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { + return std::forward_as_tuple(get(entt)); + } + + /** + * @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. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. + */ + template + value_type &emplace(const entity_type entt, Args &&...args) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + const auto it = emplace_element(entt, false, Type{std::forward(args)...}); + return element_at(static_cast(it.index())); + } else { + const auto it = emplace_element(entt, false, std::forward(args)...); + return element_at(static_cast(it.index())); + } + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the updated instance. + */ + template + value_type &patch(const entity_type entt, Func &&...func) { + const auto idx = base_type::index(entt); + auto &elem = element_at(idx); + (std::forward(func)(elem), ...); + return elem; + } + + /** + * @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. + * + * @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. + * @return Iterator pointing to the last element inserted, if any. + */ + template + iterator insert(It first, It last, const value_type &value = {}) { + for(; first != last; ++first) { + emplace_element(*first, true, value); + } + + return begin(); + } + + /** + * @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. + * @return Iterator pointing to the first element inserted, if any. + */ + template::value_type, value_type>>> + iterator insert(EIt first, EIt last, CIt from) { + for(; first != last; ++first, ++from) { + emplace_element(*first, true, *from); + } + + return begin(); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its component. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin(), rbegin()}, internal::extended_storage_iterator{base_type::rend(), rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin(), crbegin()}, internal::extended_storage_iterator{base_type::crend(), crend()}}; + } + +private: + container_type payload; +}; + +/*! @copydoc basic_storage */ +template +class basic_storage::page_size == 0u>> + : public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + +public: + /*! @brief Base type. */ + using base_type = basic_sparse_set>; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Component traits. */ + using traits_type = component_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), deletion_policy{traits_type::in_place_delete}, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + // std::allocator has no cross constructors (waiting for C++20) + if constexpr(std::is_void_v && !std::is_constructible_v) { + return allocator_type{}; + } else { + return allocator_type{base_type::get_allocator()}; + } + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + } + + /** + * @brief Returns an empty tuple. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + return std::tuple{}; + } + + /** + * @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. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + */ + template + void emplace(const entity_type entt, Args &&...) { + base_type::try_emplace(entt, false); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of optional arguments. + * @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, Args &&...) { + for(; first != last; ++first) { + base_type::try_emplace(*first, true); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}}; + } +}; + +/** + * @brief Swap-only entity storage specialization. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage + : public basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + + auto entity_at(const std::size_t pos) const noexcept { + ENTT_ASSERT(pos < underlying_type::traits_type::to_entity(null), "Invalid element"); + return underlying_type::traits_type::combine(static_cast(pos), {}); + } + +protected: + /** + * @brief Assigns an entity to a storage. + * @param hint A valid identifier. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace(const Entity hint, const bool, const void *) override { + return base_type::find(emplace(hint)); + } + +public: + /*! @brief Base type. */ + using base_type = basic_sparse_set; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Entity; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} { + } + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), deletion_policy::swap_only, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + base_type::operator=(std::move(other)); + return *this; + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + } + + /** + * @brief Returns an empty tuple. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + return std::tuple{}; + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @return A valid identifier. + */ + entity_type emplace() { + const auto len = base_type::free_list(); + const auto entt = (len == base_type::size()) ? entity_at(len) : base_type::data()[len]; + return *base_type::try_emplace(entt, true); + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * + * If the requested identifier isn't in use, the suggested one is used. + * Otherwise, a new identifier is returned. + * + * @param hint Required identifier. + * @return A valid identifier. + */ + entity_type emplace(const entity_type hint) { + if(hint == null || hint == tombstone) { + return emplace(); + } else if(const auto curr = underlying_type::traits_type::construct(underlying_type::traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) { + const auto pos = static_cast(underlying_type::traits_type::to_entity(hint)); + const auto entt = *base_type::try_emplace(hint, true); + + while(!(pos < base_type::size())) { + base_type::try_emplace(entity_at(base_type::size() - 1u), false); + } + + return entt; + } else if(const auto idx = base_type::index(curr); idx < base_type::free_list()) { + return emplace(); + } else { + return *base_type::try_emplace(hint, true); + } + } + + /** + * @brief Updates a given identifier. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns each element in a range an identifier. + * @tparam It Type of mutable 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 insert(It first, It last) { + for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) { + *first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true); + } + + for(; first != last; ++first) { + *first = *base_type::try_emplace(entity_at(base_type::free_list()), true); + } + } + + /** + * @brief Makes all elements in a range contiguous. + * @tparam It Type of forward iterator. + * @param first An iterator to the first element of the range to pack. + * @param last An iterator past the last element of the range to pack. + * @return The number of elements within the newly created range. + */ + template + [[deprecated("use sort_as instead")]] size_type pack(It first, It last) { + base_type::sort_as(first, last); + return static_cast(std::distance(first, last)); + } + + /** + * @brief Returns the number of elements considered still in use. + * @return The number of elements considered still in use. + */ + [[deprecated("use free_list() instead")]] [[nodiscard]] size_type in_use() const noexcept { + return base_type::free_list(); + } + + /** + * @brief Sets the number of elements considered still in use. + * @param len The number of elements considered still in use. + */ + [[deprecated("use free_list(len) instead")]] void in_use(const size_type len) noexcept { + base_type::free_list(len); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin(0)}, internal::extended_storage_iterator{base_type::end(0)}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin(0)}, internal::extended_storage_iterator{base_type::cend(0)}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend(0)}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend(0)}}; + } +}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class extended_group_iterator; + +template +class extended_group_iterator, get_t> { + template + auto index_to_element([[maybe_unused]] Type &cpool) const { + if constexpr(Type::traits_type::page_size == 0u) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(cpool.rbegin()[it.index()]); + } + } + +public: + using iterator_type = It; + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})..., std::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_group_iterator() + : it{}, + pools{} {} + + extended_group_iterator(iterator_type from, const std::tuple &cpools) + : it{from}, + pools{cpools} {} + + extended_group_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_group_iterator operator++(int) noexcept { + extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get(pools))..., std::get(pools)->get_as_tuple(*it)...); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +struct group_descriptor { + using size_type = std::size_t; + virtual ~group_descriptor() = default; + virtual size_type owned(const id_type *, const size_type) const noexcept { + return 0u; + } +}; + +template +class group_handler; + +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v...>, "Groups do not support in-place delete"); + static_assert(!std::disjunction_v..., std::is_const..., std::is_const...>, "Const storage type not allowed"); + + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; + + template + void swap_elements(const std::size_t pos, const entity_type entt, std::index_sequence) { + (std::get(pools)->swap_elements(std::get(pools)->data()[pos], entt), ...); + } + + void push_on_construct(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + swap_elements(len++, entt, std::index_sequence_for{}); + } + } + + void push_on_destroy(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + swap_elements(len++, entt, std::index_sequence_for{}); + } + } + + void remove_if(const entity_type entt) { + if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) { + swap_elements(--len, entt, std::index_sequence_for{}); + } + } + +public: + using size_type = typename base_type::size_type; + + group_handler(Owned &...opool, Get &...gpool, Exclude &...epool) + : pools{&opool..., &gpool...}, + filter{&epool...}, + len{} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + for(auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) { + push_on_construct(*first); + } + } + + size_type owned(const id_type *elem, const size_type length) const noexcept final { + size_type cnt = 0u; + + for(auto pos = 0u; pos < length; ++pos) { + cnt += ((elem[pos] == entt::type_hash::value()) || ...); + } + + return cnt; + } + + [[nodiscard]] size_type length() const noexcept { + return len; + } + + auto pools_as_tuple() const noexcept { + return pools; + } + + auto filter_as_tuple() const noexcept { + return filter; + } + +private: + std::tuple pools; + std::tuple filter; + std::size_t len; +}; + +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v..., std::is_const...>, "Const storage type not allowed"); + + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; + + void push_on_construct(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + elem.push(entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + elem.push(entt); + } + } + + void remove_if(const entity_type entt) { + elem.remove(entt); + } + +public: + using common_type = base_type; + + template + group_handler(const Alloc &alloc, Get &...gpool, Exclude &...epool) + : pools{&gpool...}, + filter{&epool...}, + elem{alloc} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + for(const auto entity: static_cast(*std::get<0>(pools))) { + push_on_construct(entity); + } + } + + common_type &handle() noexcept { + return elem; + } + + const common_type &handle() const noexcept { + return elem; + } + + auto pools_as_tuple() const noexcept { + return pools; + } + + auto filter_as_tuple() const noexcept { + return filter; + } + +private: + std::tuple pools; + std::tuple filter; + base_type elem; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @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 are at + * least in the given storage. Moreover, it's guaranteed that the entity list is + * tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->pools_as_tuple() : return_type{}; + } + + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->filter_as_tuple() : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler, get_t...>, exclude_t...>>; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return descriptor->handle(); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Get); + + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } + } + + /** + * @brief Returns the number of entities that are part of the group. + * @return Number of entities that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? handle().size() : size_type{}; + } + + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + [[nodiscard]] size_type capacity() const noexcept { + return *this ? handle().capacity() : size_type{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + descriptor->handle().shrink_to_fit(); + } + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || handle().empty(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? handle().begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? handle().rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return *this ? handle().find(entt) : iterator{}; + } + + /** + * @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. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(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 { + for(const auto entt: *this) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); + } else { + std::apply(func, get(entt)); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return iterable{{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * 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 Type &..., const Type &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` 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 object 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. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other 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) { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes 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(*this) { + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + descriptor->handle().sort(std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + descriptor->handle().sort(std::move(comp), std::move(algo), std::forward(args)...); + } + } + } + + /** + * @brief Sort entities according to their order in a range. + * + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. + * + * @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 sort_as(It first, It last) const { + if(*this) { + descriptor->handle().sort_as(first, last); + } + } + + /** + * @brief Sort entities according to their order in a range. + * @param other The storage to use to impose the order. + */ + [[deprecated("use iterator based sort_as instead")]] void sort_as(const common_type &other) const { + sort_as(other.begin(), other.end()); + } + +private: + handler *descriptor; +}; + +/** + * @brief Owning group. + * + * Owning groups returns all entities and only the entities that are at + * least in the given storage. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that all components in the owned storage are tightly packed + * in memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned storage and all instances have the + * same order in memory. + * + * The more types of storage are owned, the faster it is to iterate a group. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->pools_as_tuple() : return_type{}; + } + + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->filter_as_tuple() : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler...>, get_t...>, exclude_t...>>; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return *storage<0>(); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Owned) + sizeof...(Get); + + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } + } + + /** + * @brief Returns the number of entities that that are part of the group. + * @return Number of entities that that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? descriptor->length() : size_type{}; + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || !descriptor->length(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? (handle().end() - descriptor->length()) : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + const auto it = *this ? handle().find(entt) : iterator{}; + return it >= begin() ? it : iterator{}; + } + + /** + * @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. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length())); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(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 { + for(auto args: each()) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, args); + } else { + std::apply([&func](auto, auto &&...less) { func(std::forward(less)...); }, args); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return {{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * 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 Type &, const Type &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` 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 object 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. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other 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) const { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes 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) const { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward(args)...); + } + + auto cb = [this](auto *head, auto *...other) { + for(auto next = descriptor->length(); next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap_elements(other->data()[pos], entt), ...); + } + }; + + std::apply(cb, cpools); + } + +private: + handler *descriptor; +}; + +} // namespace entt + +#endif + +// #include "entity/handle.hpp" +#ifndef ENTT_ENTITY_HANDLE_HPP +#define ENTT_ENTITY_HANDLE_HPP + +#include +#include +#include +#include +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class handle_storage_iterator final { + template + friend class handle_storage_iterator; + + using underlying_type = std::remove_reference_t; + using entity_type = typename underlying_type::entity_type; + +public: + using value_type = typename std::iterator_traits::value_type; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr handle_storage_iterator() noexcept + : entt{null}, + it{}, + last{} {} + + constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept + : entt{value}, + it{from}, + last{to} { + while(it != last && !it->second.contains(entt)) { + ++it; + } + } + + constexpr handle_storage_iterator &operator++() noexcept { + while(++it != last && !it->second.contains(entt)) {} + return *this; + } + + constexpr handle_storage_iterator operator++(int) noexcept { + handle_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *it; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template + friend constexpr bool operator==(const handle_storage_iterator &, const handle_storage_iterator &) noexcept; + +private: + entity_type entt; + It it; + It last; +}; + +template +[[nodiscard]] constexpr bool operator==(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Non-owning handle to an entity. + * + * Tiny wrapper around a registry and an entity. + * + * @tparam Registry Basic registry type. + * @tparam Scope Types to which to restrict the scope of a handle. + */ +template +struct basic_handle { + /*! @brief Type of registry accepted by the handle. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename registry_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = typename registry_type::size_type; + + /*! @brief Constructs an invalid handle. */ + basic_handle() noexcept + : reg{}, + entt{null} {} + + /** + * @brief Constructs a handle from a given registry and entity. + * @param ref An instance of the registry class. + * @param value A valid identifier. + */ + basic_handle(registry_type &ref, entity_type value) noexcept + : reg{&ref}, + entt{value} {} + + /** + * @brief Returns an iterable object to use to _visit_ a handle. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage.
+ * Returned storage are those that contain the entity associated with the + * handle. + * + * @return An iterable object to use to _visit_ the handle. + */ + [[nodiscard]] auto storage() const noexcept { + auto iterable = reg->storage(); + using iterator_type = internal::handle_storage_iterator; + return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}}; + } + + /** + * @brief Constructs a const handle from a non-const one. + * @tparam Other A valid entity type. + * @tparam Args Scope of the handle to construct. + * @return A const handle referring to the same registry and the same + * entity. + */ + template + operator basic_handle() const noexcept { + static_assert(std::is_same_v || std::is_same_v, Registry>, "Invalid conversion between different handles"); + static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v, Args>))), "Invalid conversion between different handles"); + + return reg ? basic_handle{*reg, entt} : basic_handle{}; + } + + /** + * @brief Converts a handle to its underlying entity. + * @return The contained identifier. + */ + [[nodiscard]] operator entity_type() const noexcept { + return entity(); + } + + /** + * @brief Checks if a handle refers to non-null registry pointer and entity. + * @return True if the handle refers to non-null registry and entity, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return reg && reg->valid(entt); + } + + /** + * @brief Checks if a handle refers to a valid entity or not. + * @return True if the handle refers to a valid entity, false otherwise. + */ + [[nodiscard]] bool valid() const { + return reg->valid(entt); + } + + /** + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ + [[nodiscard]] registry_type *registry() const noexcept { + return reg; + } + + /** + * @brief Returns the entity associated with a handle. + * @return The entity associated with the handle. + */ + [[nodiscard]] entity_type entity() const noexcept { + return entt; + } + + /*! @brief Destroys the entity associated with a handle. */ + void destroy() { + reg->destroy(std::exchange(entt, null)); + } + + /** + * @brief Destroys the entity associated with a handle. + * @param version A desired version upon destruction. + */ + void destroy(const version_type version) { + reg->destroy(std::exchange(entt, null), version); + } + + /** + * @brief Assigns the given component to a handle. + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template emplace(entt, std::forward(args)...); + } + + /** + * @brief Assigns or replaces the given component for a handle. + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace_or_replace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template emplace_or_replace(entt, std::forward(args)...); + } + + /** + * @brief Patches the given component for a handle. + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(Func &&...func) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template patch(entt, std::forward(func)...); + } + + /** + * @brief Replaces the given component for a handle. + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ + template + decltype(auto) replace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template replace(entt, std::forward(args)...); + } + + /** + * @brief Removes the given components from a handle. + * @tparam Component Types of components to remove. + * @return The number of components actually removed. + */ + template + size_type remove() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template remove(entt); + } + + /** + * @brief Erases the given components from a handle. + * @tparam Component Types of components to erase. + */ + template + void erase() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + reg->template erase(entt); + } + + /** + * @brief Checks if a handle has all the given components. + * @tparam Component Components for which to perform the check. + * @return True if the handle has all the components, false otherwise. + */ + template + [[nodiscard]] decltype(auto) all_of() const { + return reg->template all_of(entt); + } + + /** + * @brief Checks if a handle has at least one of the given components. + * @tparam Component Components for which to perform the check. + * @return True if the handle has at least one of the given components, + * false otherwise. + */ + template + [[nodiscard]] decltype(auto) any_of() const { + return reg->template any_of(entt); + } + + /** + * @brief Returns references to the given components for a handle. + * @tparam Component Types of components to get. + * @return References to the components owned by the handle. + */ + template + [[nodiscard]] decltype(auto) get() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template get(entt); + } + + /** + * @brief Returns a reference to the given component for a handle. + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the handle. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template get_or_emplace(entt, std::forward(args)...); + } + + /** + * @brief Returns pointers to the given components for a handle. + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the handle. + */ + template + [[nodiscard]] auto try_get() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template try_get(entt); + } + + /** + * @brief Checks if a handle has components assigned. + * @return True if the handle has no components assigned, false otherwise. + */ + [[nodiscard]] bool orphan() const { + return reg->orphan(entt); + } + +private: + registry_type *reg; + entity_type entt; +}; + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same registry and the same + * entity, false otherwise. + */ +template +[[nodiscard]] bool operator==(const basic_handle &lhs, const basic_handle &rhs) noexcept { + return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity(); +} + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same registry and the same + * entity, true otherwise. + */ +template +[[nodiscard]] bool operator!=(const basic_handle &lhs, const basic_handle &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace entt + +#endif + +// #include "entity/helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/type_traits.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 + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + +#include + +namespace entt { + +template +class delegate; + +template> +class basic_dispatcher; + +template> +class emitter; + +class connection; + +struct scoped_connection; + +template +class sink; + +template> +class sigh; + +/*! @brief Alias declaration for the most common use case. */ +using dispatcher = basic_dispatcher<>; + +/*! @brief Disambiguation tag for constructors and the like. */ +template +struct connect_arg_t { + /*! @brief Default constructor. */ + explicit connect_arg_t() = default; +}; + +/** + * @brief Constant of type connect_arg_t used to disambiguate calls. + * @tparam Candidate Element to connect (likely a free or member function). + */ +template +inline constexpr connect_arg_t connect_arg{}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 { + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() noexcept + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) noexcept { + connect(function, payload); + } + + /** + * @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() noexcept { + instance = 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) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(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) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(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 an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @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. + * + * @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(static_cast(*this), "Uninitialized delegate"); + return fn(instance, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + // no need to also test instance + 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. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance; + function_type *fn; +}; + +/** + * @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 +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> 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 &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class extended_group_iterator; + +template +class extended_group_iterator, get_t> { + template + auto index_to_element([[maybe_unused]] Type &cpool) const { + if constexpr(Type::traits_type::page_size == 0u) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(cpool.rbegin()[it.index()]); + } + } + +public: + using iterator_type = It; + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})..., std::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_group_iterator() + : it{}, + pools{} {} + + extended_group_iterator(iterator_type from, const std::tuple &cpools) + : it{from}, + pools{cpools} {} + + extended_group_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_group_iterator operator++(int) noexcept { + extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get(pools))..., std::get(pools)->get_as_tuple(*it)...); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +struct group_descriptor { + using size_type = std::size_t; + virtual ~group_descriptor() = default; + virtual size_type owned(const id_type *, const size_type) const noexcept { + return 0u; + } +}; + +template +class group_handler; + +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v...>, "Groups do not support in-place delete"); + static_assert(!std::disjunction_v..., std::is_const..., std::is_const...>, "Const storage type not allowed"); + + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; + + template + void swap_elements(const std::size_t pos, const entity_type entt, std::index_sequence) { + (std::get(pools)->swap_elements(std::get(pools)->data()[pos], entt), ...); + } + + void push_on_construct(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + swap_elements(len++, entt, std::index_sequence_for{}); + } + } + + void push_on_destroy(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + swap_elements(len++, entt, std::index_sequence_for{}); + } + } + + void remove_if(const entity_type entt) { + if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) { + swap_elements(--len, entt, std::index_sequence_for{}); + } + } + +public: + using size_type = typename base_type::size_type; + + group_handler(Owned &...opool, Get &...gpool, Exclude &...epool) + : pools{&opool..., &gpool...}, + filter{&epool...}, + len{} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + for(auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) { + push_on_construct(*first); + } + } + + size_type owned(const id_type *elem, const size_type length) const noexcept final { + size_type cnt = 0u; + + for(auto pos = 0u; pos < length; ++pos) { + cnt += ((elem[pos] == entt::type_hash::value()) || ...); + } + + return cnt; + } + + [[nodiscard]] size_type length() const noexcept { + return len; + } + + auto pools_as_tuple() const noexcept { + return pools; + } + + auto filter_as_tuple() const noexcept { + return filter; + } + +private: + std::tuple pools; + std::tuple filter; + std::size_t len; +}; + +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v..., std::is_const...>, "Const storage type not allowed"); + + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; + + void push_on_construct(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + elem.push(entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + elem.push(entt); + } + } + + void remove_if(const entity_type entt) { + elem.remove(entt); + } + +public: + using common_type = base_type; + + template + group_handler(const Alloc &alloc, Get &...gpool, Exclude &...epool) + : pools{&gpool...}, + filter{&epool...}, + elem{alloc} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + for(const auto entity: static_cast(*std::get<0>(pools))) { + push_on_construct(entity); + } + } + + common_type &handle() noexcept { + return elem; + } + + const common_type &handle() const noexcept { + return elem; + } + + auto pools_as_tuple() const noexcept { + return pools; + } + + auto filter_as_tuple() const noexcept { + return filter; + } + +private: + std::tuple pools; + std::tuple filter; + base_type elem; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @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 are at + * least in the given storage. Moreover, it's guaranteed that the entity list is + * tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->pools_as_tuple() : return_type{}; + } + + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->filter_as_tuple() : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler, get_t...>, exclude_t...>>; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return descriptor->handle(); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Get); + + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } + } + + /** + * @brief Returns the number of entities that are part of the group. + * @return Number of entities that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? handle().size() : size_type{}; + } + + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + [[nodiscard]] size_type capacity() const noexcept { + return *this ? handle().capacity() : size_type{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + descriptor->handle().shrink_to_fit(); + } + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || handle().empty(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? handle().begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? handle().rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return *this ? handle().find(entt) : iterator{}; + } + + /** + * @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. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(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 { + for(const auto entt: *this) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); + } else { + std::apply(func, get(entt)); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return iterable{{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * 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 Type &..., const Type &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` 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 object 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. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other 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) { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes 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(*this) { + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + descriptor->handle().sort(std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + descriptor->handle().sort(std::move(comp), std::move(algo), std::forward(args)...); + } + } + } + + /** + * @brief Sort entities according to their order in a range. + * + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. + * + * @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 sort_as(It first, It last) const { + if(*this) { + descriptor->handle().sort_as(first, last); + } + } + + /** + * @brief Sort entities according to their order in a range. + * @param other The storage to use to impose the order. + */ + [[deprecated("use iterator based sort_as instead")]] void sort_as(const common_type &other) const { + sort_as(other.begin(), other.end()); + } + +private: + handler *descriptor; +}; + +/** + * @brief Owning group. + * + * Owning groups returns all entities and only the entities that are at + * least in the given storage. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that all components in the owned storage are tightly packed + * in memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned storage and all instances have the + * same order in memory. + * + * The more types of storage are owned, the faster it is to iterate a group. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->pools_as_tuple() : return_type{}; + } + + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->filter_as_tuple() : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler...>, get_t...>, exclude_t...>>; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return *storage<0>(); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Owned) + sizeof...(Get); + + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } + } + + /** + * @brief Returns the number of entities that that are part of the group. + * @return Number of entities that that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? descriptor->length() : size_type{}; + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || !descriptor->length(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? (handle().end() - descriptor->length()) : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + const auto it = *this ? handle().find(entt) : iterator{}; + return it >= begin() ? it : iterator{}; + } + + /** + * @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. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length())); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(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 { + for(auto args: each()) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, args); + } else { + std::apply([&func](auto, auto &&...less) { func(std::forward(less)...); }, args); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return {{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * 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 Type &, const Type &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` 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 object 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. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other 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) const { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes 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) const { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward(args)...); + } + + auto cb = [this](auto *head, auto *...other) { + for(auto next = descriptor->length(); next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap_elements(other->data()[pos], entt), ...); + } + }; + + std::apply(cb, cpools); + } + +private: + handler *descriptor; +}; + +} // namespace entt + +#endif + +// #include "storage.hpp" + +// #include "view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +[[nodiscard]] bool all_of_but(const std::size_t index, const Type *const *it, const std::size_t len, const Entity entt) noexcept { + std::size_t pos{}; + for(; (pos != index) && it[pos]->contains(entt); ++pos) {} + + if(pos == index) { + for(++pos; (pos != len) && it[pos]->contains(entt); ++pos) {} + } + + return pos == len; +} + +template +[[nodiscard]] bool none_of(const Type *const *it, const std::size_t len, const Entity entt) noexcept { + std::size_t pos{}; + for(; (pos != len) && !(it[pos] && it[pos]->contains(entt)); ++pos) {} + return pos == len; +} + +template +[[nodiscard]] bool fully_initialized(const Type *const *it, const std::size_t len) noexcept { + std::size_t pos{}; + for(; (pos != len) && it[pos]; ++pos) {} + return pos == len; +} + +template +[[nodiscard]] Result view_pack(const View &view, const Other &other, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence) { + Result elem{}; + // friend-initialization, avoid multiple calls to refresh + elem.pools = {view.template storage()..., other.template storage()...}; + elem.filter = {view.template storage()..., other.template storage()...}; + elem.refresh(); + return elem; +} + +template +class view_iterator final { + using iterator_type = typename Type::const_iterator; + + [[nodiscard]] bool valid(const typename iterator_type::value_type entt) const noexcept { + return ((Get != 1u) || (entt != tombstone)) && all_of_but(index, pools.data(), Get, entt) && none_of(filter.data(), Exclude, entt); + } + +public: + using value_type = typename iterator_type::value_type; + using pointer = typename iterator_type::pointer; + using reference = typename iterator_type::reference; + using difference_type = typename iterator_type::difference_type; + using iterator_category = std::forward_iterator_tag; + + constexpr view_iterator() noexcept + : it{}, + last{}, + pools{}, + filter{}, + index{} {} + + view_iterator(iterator_type curr, iterator_type to, std::array value, std::array excl, const std::size_t idx) noexcept + : it{curr}, + last{to}, + pools{value}, + filter{excl}, + index{idx} { + while(it != last && !valid(*it)) { + ++it; + } + } + + view_iterator &operator++() noexcept { + while(++it != last && !valid(*it)) {} + return *this; + } + + view_iterator operator++(int) noexcept { + view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return &*it; + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; + +private: + iterator_type it; + iterator_type last; + std::array pools; + std::array filter; + std::size_t index; +}; + +template +[[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +struct extended_view_iterator final { + using iterator_type = It; + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_view_iterator() + : it{}, + pools{} {} + + extended_view_iterator(iterator_type from, std::tuple value) + : it{from}, + pools{value} {} + + extended_view_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_view_iterator operator++(int) noexcept { + extended_view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief View implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + * + * @b Important + * + * View iterators aren't invalidated if: + * + * * New elements are added to the storage iterated by the view. + * * The entity currently returned is modified (for example, components are + * added or removed from it). + * * The entity currently returned is destroyed. + * + * In all other cases, modifying the storage iterated by a view in any way can + * invalidate all iterators. + */ +template +class basic_view; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + * @tparam Get Number of storage iterated by the view. + * @tparam Exclude Number of storage used to filter the view. + */ +template +class basic_common_view { + template + friend Return internal::view_pack(const View &, const Other &, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence); + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_common_view() noexcept = default; + + basic_common_view(std::array value, std::array excl) noexcept + : pools{value}, + filter{excl}, + leading{}, + index{Get} { + unchecked_refresh(); + } + + void use(const std::size_t pos) noexcept { + if(leading) { + index = pos; + leading = pools[index]; + } + } + + void unchecked_refresh() noexcept { + index = 0u; + + for(size_type pos{1u}; pos < Get; ++pos) { + if(pools[pos]->size() < pools[index]->size()) { + index = pos; + } + } + + leading = pools[index]; + } + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::view_iterator; + + /*! @brief Updates the internal leading view if required. */ + void refresh() noexcept { + size_type pos = (leading != nullptr) * Get; + for(; pos < Get && pools[pos] != nullptr; ++pos) {} + + if(pos == Get) { + unchecked_refresh(); + } + } + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return leading; + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const noexcept { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const noexcept { + return leading ? iterator{leading->begin(0), leading->end(0), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return leading ? iterator{leading->end(0), leading->end(0), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + if(leading) { + auto it = leading->rbegin(0); + const auto last = leading->rend(0); + for(; it != last && !contains(*it); ++it) {} + return it == last ? null : *it; + } + + return null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return contains(entt) ? iterator{leading->find(entt), leading->end(), pools, filter, index} : end(); + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return leading && internal::fully_initialized(filter.data(), Exclude); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + if(leading) { + const auto idx = leading->find(entt).index(); + return (!(idx < 0 || idx > leading->begin(0).index())) && internal::all_of_but(index, pools.data(), Get, entt) && internal::none_of(filter.data(), Exclude, entt); + } + + return false; + } + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + std::array pools{}; + std::array filter{}; + const common_type *leading{}; + size_type index{Get}; + /*! @endcond */ +}; + +/** + * @brief General purpose view. + * + * This view visits all entities that are at least in the given storage. During + * initialization, it also looks at the number of elements available for each + * storage and uses the smallest set in order to get a performance boost. + * + * @sa basic_view + * + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template +class basic_view, exclude_t>: public basic_common_view, sizeof...(Get), sizeof...(Exclude)> { + using base_type = basic_common_view, sizeof...(Get), sizeof...(Exclude)>; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + template + auto storage(std::index_sequence) const noexcept { + return std::make_tuple(storage()...); + } + + template + [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { + if constexpr(Curr == Other) { + return std::forward_as_tuple(std::get(curr)...); + } else { + return storage()->get_as_tuple(std::get<0>(curr)); + } + } + + template + void each(Func &func, std::index_sequence) const { + for(const auto curr: storage()->each()) { + if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && internal::all_of_but(this->index, this->pools.data(), sizeof...(Get), entt) && internal::none_of(this->filter.data(), sizeof...(Exclude), entt)) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); + } else { + std::apply(func, std::tuple_cat(dispatch_get(curr)...)); + } + } + } + } + + template + void pick_and_each(Func &func, std::index_sequence seq) const { + ((storage() == base_type::handle() ? each(func, seq) : void()), ...); + } + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Bidirectional iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Iterable view type. */ + using iterable = iterable_adaptor>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(Get &...value, Exclude &...excl) noexcept + : base_type{{&value...}, {&excl...}} { + } + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(std::tuple value, std::tuple excl = {}) noexcept + : basic_view{std::make_from_tuple(std::tuple_cat(value, excl))} {} + + /** + * @brief Forces a view to use a given component to drive iterations + * @tparam Type Type of component to use to drive iterations. + */ + template + void use() noexcept { + use>(); + } + + /** + * @brief Forces a view to use a given component to drive iterations + * @tparam Index Index of the component to use to drive iterations. + */ + template + void use() noexcept { + base_type::use(Index); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t>; + + if constexpr(Index < sizeof...(Get)) { + return static_cast(const_cast *>(this->pools[Index])); + } else { + return static_cast(const_cast *>(this->filter[Index - sizeof...(Get)])); + } + } + + /** + * @brief Assigns a storage to a view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + storage>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + static_assert(std::is_convertible_v> &>, "Unexpected type"); + + if constexpr(Index < sizeof...(Get)) { + this->pools[Index] = &elem; + base_type::refresh(); + } else { + this->filter[Index - sizeof...(Get)] = &elem; + } + } + + /** + * @brief Returns the components assigned to the given entity. + * @param entt A valid identifier. + * @return The components assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return get(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, storage(std::index_sequence_for{})); + } else if constexpr(sizeof...(Index) == 1) { + return (storage()->get(entt), ...); + } else { + return std::tuple_cat(storage()->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if(base_type::handle() != nullptr) { + pick_and_each(func, std::index_sequence_for{}); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + const auto as_pools = storage(std::index_sequence_for{}); + return {internal::extended_view_iterator{base_type::begin(), as_pools}, internal::extended_view_iterator{base_type::end(), as_pools}}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Component list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + */ +template +class basic_storage_view { +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_storage_view() noexcept = default; + + basic_storage_view(const Type *value) noexcept + : leading{value} {} + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename common_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return leading; + } + + /** + * @brief Returns the number of entities that have the given component. + * @return Number of entities that have the given component. + */ + [[nodiscard]] size_type size() const noexcept { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !leading || leading->empty(); + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const noexcept { + return leading ? leading->begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return leading ? leading->end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed view. + * + * If the view is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed view. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return leading ? leading->rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return leading ? leading->rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + return empty() ? null : *leading->begin(); + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + return empty() ? null : *leading->rbegin(); + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return leading ? leading->find(entt) : iterator{}; + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (leading != nullptr); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return leading && leading->contains(entt); + } + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + const common_type *leading{}; + /*! @endcond */ +}; + +/** + * @brief Storage view specialization. + * + * This specialization offers a boost in terms of performance. It can access the + * underlying data structure directly and avoid superfluous checks. + * + * @sa basic_view + * + * @tparam Get Type of storage iterated by the view. + */ +template +class basic_view, exclude_t<>, std::void_t>>: public basic_storage_view { + using base_type = basic_storage_view; + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Random access iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename base_type::reverse_iterator; + /*! @brief Iterable view type. */ + using iterable = decltype(std::declval().each()); + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(Get &value) noexcept + : base_type{&value} { + } + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(std::tuple value, std::tuple<> = {}) noexcept + : basic_view{std::get<0>(value)} {} + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return storage<0>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(Index == 0u, "Index out of bounds"); + return static_cast(const_cast *>(this->leading)); + } + + /** + * @brief Assigns a storage to a view. + * @param elem A storage to assign to the view. + */ + void storage(Get &elem) noexcept { + storage<0>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Get &elem) noexcept { + static_assert(Index == 0u, "Index out of bounds"); + this->leading = &elem; + } + + /** + * @brief Returns the component assigned to the given entity. + * @param entt A valid identifier. + * @return The component assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return storage()->get(entt); + } + + /** + * @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. + */ + [[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type operator[](const size_type pos) const { + return base_type::begin()[pos]; + } + + /** + * @brief Returns the component assigned to the given entity. + * @tparam Elem Type of the component to get. + * @param entt A valid identifier. + * @return The component assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return get<0>(entt); + } + + /** + * @brief Returns the component assigned to the given entity. + * @tparam Index Index of the component to get. + * @param entt A valid identifier. + * @return The component assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return storage()->get_as_tuple(entt); + } else { + return storage()->get(entt); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &); + * void(typename Type &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if(auto *elem = storage(); elem) { + if constexpr(is_applicable_veach().begin())>) { + for(const auto pack: elem->each()) { + std::apply(func, pack); + } + } else if constexpr(std::is_invocable_vbegin())>) { + for(auto &&component: *elem) { + func(component); + } + } else { + for(size_type pos = elem->size(); pos; --pos) { + func(); + } + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its component if it's a non-empty one. The _constness_ of + * the component is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + auto *elem = storage(); + return elem ? elem->each() : iterable{}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Component list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for<>{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template +basic_view(Type &...storage) -> basic_view, exclude_t<>>; + +/** + * @brief Deduction guide. + * @tparam Get Types of components iterated by the view. + * @tparam Exclude Types of components used to filter the view. + */ +template +basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Registry Basic registry type. + */ +template +class as_view { + template + auto dispatch(get_t, exclude_t) const { + return reg.template view...>(exclude_t...>{}); + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Get Type of storage used to construct the view. + * @tparam Exclude Types of storage used to filter the view. + * @return A newly created view. + */ + template + operator basic_view() const { + return dispatch(Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Converts a registry to a group. + * @tparam Registry Basic registry type. + */ +template +class as_group { + template + auto dispatch(owned_t, get_t, exclude_t) const { + if constexpr(std::is_const_v) { + return reg.template group_if_exists(get_t{}, exclude_t{}); + } else { + return reg.template group...>(get_t...>{}, exclude_t...>{}); + } + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Owned Types of _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + * @return A newly created group. + */ + template + operator basic_group() const { + return dispatch(Owned{}, Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @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 Registry Basic registry type. + * @param reg A registry that contains the given entity and its components. + * @param entt Entity from which to get the component. + */ +template>> +void invoke(Registry ®, const typename Registry::entity_type entt) { + static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); + delegate func; + func.template connect(reg.template get>(entt)); + func(reg, entt); +} + +/** + * @brief Returns the entity associated with a given component. + * + * @warning + * Currently, this function only works correctly with the default storage as it + * makes assumptions about how the components are laid out. + * + * @tparam Args Storage type template parameters. + * @param storage A storage that contains the given component. + * @param instance A valid component instance. + * @return The entity associated with the given component. + */ +template +auto to_entity(const basic_storage &storage, const typename basic_storage::value_type &instance) -> typename basic_storage::entity_type { + constexpr auto page_size = basic_storage::traits_type::page_size; + const typename basic_storage::base_type &base = storage; + const auto *addr = std::addressof(instance); + + for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) { + if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast(page_size)) { + return *(it + dist); + } + } + + return null; +} + +/** + * @copybrief to_entity + * @tparam Args Registry type template parameters. + * @tparam Component Type of component. + * @param reg A registry that contains the given entity and its components. + * @param instance A valid component instance. + * @return The entity associated with the given component. + */ +template +[[deprecated("use storage based to_entity instead")]] typename basic_registry::entity_type to_entity(const basic_registry ®, const Component &instance) { + if(const auto *storage = reg.template storage(); storage) { + return to_entity(*storage, instance); + } + + return null; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct sigh_helper; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + */ +template +struct sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + */ + sigh_helper(registry_type &ref) + : bucket{&ref} {} + + /** + * @brief Binds a properly initialized helper to a given signal type. + * @tparam Type Type of signal to bind the helper to. + * @param id Optional name for the underlying storage to use. + * @return A helper for a given registry and signal type. + */ + template + auto with(const id_type id = type_hash::value()) noexcept { + return sigh_helper{*bucket, id}; + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + [[nodiscard]] registry_type ®istry() noexcept { + return *bucket; + } + +private: + registry_type *bucket; +}; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + * @tparam Type Type of signal to connect listeners to. + */ +template +struct sigh_helper final: sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + * @param id Optional name for the underlying storage to use. + */ + sigh_helper(registry_type &ref, const id_type id = type_hash::value()) + : sigh_helper{ref}, + name{id} {} + + /** + * @brief Forwards the call to `on_construct` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_construct(Args &&...args) { + this->registry().template on_construct(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_update` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_update(Args &&...args) { + this->registry().template on_update(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_destroy` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_destroy(Args &&...args) { + this->registry().template on_destroy(name).template connect(std::forward(args)...); + return *this; + } + +private: + id_type name; +}; + +/** + * @brief Deduction guide. + * @tparam Registry Basic registry type. + */ +template +sigh_helper(Registry &) -> sigh_helper; + +} // namespace entt + +#endif + +// #include "entity/mixin.hpp" +#ifndef ENTT_ENTITY_MIXIN_HPP +#define ENTT_ENTITY_MIXIN_HPP + +#include +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include +#include +#include +#include +#include +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 { + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() noexcept + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) noexcept { + connect(function, payload); + } + + /** + * @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() noexcept { + instance = 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) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(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) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(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 an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @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. + * + * @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(static_cast(*this), "Uninitialized delegate"); + return fn(instance, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + // no need to also test instance + 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. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance; + function_type *fn; +}; + +/** + * @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 +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> 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 &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +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 Type A valid signal handler 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 Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh { + friend class sink>; + + using alloc_traits = std::allocator_traits; + using delegate_type = delegate; + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink>; + + /*! @brief Default constructor. */ + sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) + : calls{other.calls} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) + : calls{std::move(other.calls)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{std::move(other.calls), allocator} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ + sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { + calls = std::move(other.calls); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ + void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return calls.get_allocator(); + } + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const 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. + */ + [[nodiscard]] bool empty() const 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 pos = calls.size(); pos; --pos) { + calls[pos - 1u](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 pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + + if constexpr(std::is_invocable_r_v) { + if(func()) { + break; + } + } else { + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(calls[pos - 1u](args...))) { + break; + } + } else { + func(calls[pos - 1u](args...)); + } + } + } + } + +private: + container_type 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 { + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} {} + +public: + /*! @brief Default constructor. */ + connection() + : disconnect{}, + signal{} {} + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const 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 Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) noexcept + : conn{std::exchange(other.conn, {})} {} + + /*! @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 Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ + scoped_connection &operator=(scoped_connection &&other) noexcept { + conn = std::exchange(other.conn, {}); + return *this; + } + + /** + * @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. + */ + [[nodiscard]] explicit operator bool() const 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. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sink> { + using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; + using difference_type = typename signal_type::container_type::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(); + } + + template + void disconnect_if(Func callback) { + for(auto pos = signal->calls.size(); pos; --pos) { + if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { + elem = std::move(signal->calls.back()); + signal->calls.pop_back(); + } + } + } + +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) noexcept + : 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. + */ + [[nodiscard]] bool empty() const noexcept { + return signal->calls.empty(); + } + + /** + * @brief Connects a free function (with or without payload), a bound or an + * unbound member to a signal. + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&...value_or_instance) { + disconnect(value_or_instance...); + + delegate_type call{}; + call.template connect(value_or_instance...); + signal->calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance...); + return {std::move(conn), signal}; + } + + /** + * @brief Disconnects a free function (with or without payload), a bound or + * an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + */ + template + void disconnect(Type &&...value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance...); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @param value_or_instance A valid object that fits the purpose. + */ + void disconnect(const void *value_or_instance) { + if(value_or_instance) { + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); + } + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler 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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +sink(sigh &) -> sink>; + +} // namespace entt + +#endif + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Mixin type used to add signal support to storage types. + * + * The function type of a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * This applies to all signals made available. + * + * @tparam Type Underlying storage type. + * @tparam Registry Basic registry type. + */ +template +class basic_sigh_mixin final: public Type { + using underlying_type = Type; + using owner_type = Registry; + + using basic_registry_type = basic_registry; + using sigh_type = sigh; + using underlying_iterator = typename underlying_type::base_type::basic_iterator; + + static_assert(std::is_base_of_v, "Invalid registry type"); + + owner_type &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast(*owner); + } + + void pop(underlying_iterator first, underlying_iterator last) final { + if(auto ® = owner_or_assert(); destruction.empty()) { + underlying_type::pop(first, last); + } else { + for(; first != last; ++first) { + const auto entt = *first; + destruction.publish(reg, entt); + const auto it = underlying_type::find(entt); + underlying_type::pop(it, it + 1u); + } + } + } + + void pop_all() final { + if(auto ® = owner_or_assert(); !destruction.empty()) { + for(auto it = underlying_type::base_type::begin(0), last = underlying_type::base_type::end(0); it != last; ++it) { + if constexpr(std::is_same_v) { + destruction.publish(reg, *it); + } else { + if constexpr(underlying_type::traits_type::in_place_delete) { + if(const auto entt = *it; entt != tombstone) { + destruction.publish(reg, entt); + } + } else { + destruction.publish(reg, *it); + } + } + } + } + + underlying_type::pop_all(); + } + + underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final { + const auto it = underlying_type::try_emplace(entt, force_back, value); + + if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) { + construction.publish(reg, *it); + } + + return it; + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = owner_type; + + /*! @brief Default constructor. */ + basic_sigh_mixin() + : basic_sigh_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sigh_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{}, + construction{allocator}, + destruction{allocator}, + update{allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sigh_mixin(basic_sigh_mixin &&other) noexcept + : underlying_type{std::move(other)}, + owner{other.owner}, + construction{std::move(other.construction)}, + destruction{std::move(other.destruction)}, + update{std::move(other.update)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) noexcept + : underlying_type{std::move(other), allocator}, + owner{other.owner}, + construction{std::move(other.construction), allocator}, + destruction{std::move(other.destruction), allocator}, + update{std::move(other.update), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept { + underlying_type::operator=(std::move(other)); + owner = other.owner; + construction = std::move(other.construction); + destruction = std::move(other.destruction); + update = std::move(other.update); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_sigh_mixin &other) { + using std::swap; + underlying_type::swap(other); + swap(owner, other.owner); + swap(construction, other.construction); + swap(destruction, other.destruction); + swap(update, other.update); + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.
+ * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() noexcept { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() noexcept { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() noexcept { + return sink{destruction}; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @return A return value as returned by the underlying storage. + */ + auto emplace() { + const auto entt = underlying_type::emplace(); + construction.publish(owner_or_assert(), entt); + return entt; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam Args Types of arguments to forward to the underlying storage. + * @param hint A valid identifier. + * @param args Parameters to forward to the underlying storage. + * @return A return value as returned by the underlying storage. + */ + template + decltype(auto) emplace(const entity_type hint, Args &&...args) { + if constexpr(std::is_same_v) { + const auto entt = underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), entt); + return entt; + } else { + underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), hint); + return this->get(hint); + } + } + + /** + * @brief Patches the given instance for an entity. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template + decltype(auto) patch(const entity_type entt, Func &&...func) { + underlying_type::patch(entt, std::forward(func)...); + update.publish(owner_or_assert(), entt); + return this->get(entt); + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam It Iterator type (as required by the underlying storage type). + * @tparam Args Types of arguments to forward to the underlying storage. + * @param first An iterator to the first element of the range. + * @param last An iterator past the last element of the range. + * @param args Parameters to use to forward to the underlying storage. + */ + template + void insert(It first, It last, Args &&...args) { + auto from = underlying_type::size(); + underlying_type::insert(first, last, std::forward(args)...); + + if(auto ® = owner_or_assert(); !construction.empty()) { + for(const auto to = underlying_type::size(); from != to; ++from) { + construction.publish(reg, underlying_type::operator[](from)); + } + } + } + + /** + * @brief Forwards variables to derived classes, if any. + * @param value A variable wrapped in an opaque container. + */ + void bind(any value) noexcept final { + auto *reg = any_cast(&value); + owner = reg ? reg : owner; + underlying_type::bind(std::move(value)); + } + +private: + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; +}; + +} // namespace entt + +#endif + +// #include "entity/observer.hpp" +#ifndef ENTT_ENTITY_OBSERVER_HPP +#define ENTT_ENTITY_OBSERVER_HPP + +#include +#include +#include +#include +#include +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "fwd.hpp" + +// #include "storage.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 = exclude_t{}) 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() noexcept { + return basic_collector, type_list<>, AnyOf>>{}; + } +}; + +/** + * @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 = exclude_t{}) 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() noexcept { + return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; + } + + /** + * @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 = exclude_t{}) 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. + * + * @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 Registry Basic registry type. + * @tparam Mask Mask type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_observer: private basic_storage { + using base_type = basic_storage; + + template + struct matcher_handler; + + template + struct matcher_handler, type_list, AnyOf>> { + template + static void maybe_valid_if(basic_observer &obs, Registry ®, const typename Registry::entity_type entt) { + if(reg.template all_of(entt) && !reg.template any_of(entt)) { + if(!obs.contains(entt)) { + obs.emplace(entt); + } + + obs.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) { + if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) { + obs.erase(entt); + } + } + + template + static void connect(basic_observer &obs, 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, 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, Registry ®, const typename Registry::entity_type entt) { + auto condition = [®, entt]() { + if constexpr(sizeof...(Ignore) == 0) { + return reg.template all_of(entt) && !reg.template any_of(entt); + } else { + return reg.template all_of(entt) && ((std::is_same_v || !reg.template any_of(entt)) && ...) && !reg.template any_of(entt); + } + }; + + if(condition()) { + if(!obs.contains(entt)) { + obs.emplace(entt); + } + + obs.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) { + if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) { + obs.erase(entt); + } + } + + template + static void connect(basic_observer &obs, 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, 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(Registry ®, basic_observer &obs) { + (matcher_handler::disconnect(obs, reg), ...); + } + + template + void connect(Registry ®, std::index_sequence) { + static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); + (matcher_handler::template connect(*this, reg), ...); + release.template connect<&basic_observer::disconnect>(reg); + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = typename registry_type::common_type::iterator; + + /*! @brief Default constructor. */ + basic_observer() + : basic_observer{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_observer(const allocator_type &allocator) + : base_type{allocator}, + release{} {} + + /*! @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. + * @param allocator The allocator to use. + */ + template + basic_observer(registry_type ®, basic_collector, const allocator_type &allocator = allocator_type{}) + : basic_observer{allocator} { + connect(reg, std::index_sequence_for{}); + } + + /** + * @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(registry_type ®, basic_collector) { + disconnect(); + connect(reg, std::index_sequence_for{}); + base_type::clear(); + } + + /*! @brief Disconnects an observer from the registry it keeps track of. */ + void disconnect() { + if(release) { + release(*this); + release.reset(); + } + } + + /** + * @brief Returns the number of elements in an observer. + * @return Number of elements. + */ + [[nodiscard]] size_type size() const noexcept { + return base_type::size(); + } + + /** + * @brief Checks whether an observer is empty. + * @return True if the observer is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return base_type::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 + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type *data() const noexcept { + return base_type::data(); + } + + /** + * @brief Returns an iterator to the first entity of the observer. + * + * If the observer is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the observer. + */ + [[nodiscard]] iterator begin() const noexcept { + return base_type::base_type::begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the observer. + * @return An iterator to the entity following the last entity of the + * observer. + */ + [[nodiscard]] iterator end() const noexcept { + return base_type::base_type::end(); + } + + /*! @brief Clears the underlying container. */ + void clear() noexcept { + base_type::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 { + 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: + delegate release; +}; + +} // namespace entt + +#endif + +// #include "entity/organizer.hpp" +#ifndef ENTT_ENTITY_ORGANIZER_HPP +#define ENTT_ENTITY_ORGANIZER_HPP + +#include +#include +#include +#include +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "../graph/adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_GRAPH_FWD_HPP +#define ENTT_GRAPH_FWD_HPP + +#include +#include +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Undirected graph category tag. */ +struct directed_tag {}; + +/*! @brief Directed graph category tag. */ +struct undirected_tag: directed_tag {}; + +template> +class adjacency_matrix; + +template> +class basic_flow; + +/*! @brief Alias declaration for the most common use case. */ +using flow = basic_flow<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; + } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "../graph/flow.hpp" +#ifndef ENTT_GRAPH_FLOW_HPP +#define ENTT_GRAPH_FLOW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it->second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[offset].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[offset].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; + } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class for creating task graphs. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_flow { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using task_container_type = dense_set, typename alloc_traits::template rebind_alloc>; + using ro_rw_container_type = std::vector, typename alloc_traits::template rebind_alloc>>; + using deps_container_type = dense_map, typename alloc_traits::template rebind_alloc>>; + using adjacency_matrix_type = adjacency_matrix>; + + void emplace(const id_type res, const bool is_rw) { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + + if(!deps.contains(res) && sync_on != vertices.size()) { + deps[res].emplace_back(sync_on, true); + } + + deps[res].emplace_back(index.first(), is_rw); + } + + void setup_graph(adjacency_matrix_type &matrix) const { + for(const auto &elem: deps) { + const auto last = elem.second.cend(); + auto it = elem.second.cbegin(); + + while(it != last) { + if(it->second) { + // rw item + if(auto curr = it++; it != last) { + if(it->second) { + matrix.insert(curr->first, it->first); + } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + matrix.insert(it->first, next->first); + } + } else { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + } + } + } + } else { + // ro item (first iteration only) + if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(it->first, next->first); + } + } else { + it = last; + } + } + } + } + } + + void transitive_closure(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vk{}; vk < length; ++vk) { + for(std::size_t vi{}; vi < length; ++vi) { + for(std::size_t vj{}; vj < length; ++vj) { + if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { + matrix.insert(vi, vj); + } + } + } + } + } + + void transitive_reduction(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vert{}; vert < length; ++vert) { + matrix.erase(vert, vert); + } + + for(std::size_t vj{}; vj < length; ++vj) { + for(std::size_t vi{}; vi < length; ++vi) { + if(matrix.contains(vi, vj)) { + for(std::size_t vk{}; vk < length; ++vk) { + if(matrix.contains(vj, vk)) { + matrix.erase(vi, vk); + } + } + } + } + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Iterable task list. */ + using iterable = iterable_adaptor; + /*! @brief Adjacency matrix type. */ + using graph_type = adjacency_matrix_type; + + /*! @brief Default constructor. */ + basic_flow() + : basic_flow{allocator_type{}} {} + + /** + * @brief Constructs a flow builder with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_flow(const allocator_type &allocator) + : index{0u, allocator}, + vertices{allocator}, + deps{allocator}, + sync_on{} {} + + /*! @brief Default copy constructor. */ + basic_flow(const basic_flow &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_flow(const basic_flow &other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{other.vertices, allocator}, + deps{other.deps, allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default move constructor. */ + basic_flow(basic_flow &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_flow(basic_flow &&other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{std::move(other.vertices), allocator}, + deps{std::move(other.deps), allocator}, + sync_on{other.sync_on} {} + + /** + * @brief Default copy assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(const basic_flow &) = default; + + /** + * @brief Default move assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(basic_flow &&) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return allocator_type{index.second()}; + } + + /** + * @brief Returns the identifier at specified location. + * @param pos Position of the identifier to return. + * @return The requested identifier. + */ + [[nodiscard]] id_type operator[](const size_type pos) const { + return vertices.cbegin()[pos]; + } + + /*! @brief Clears the flow builder. */ + void clear() noexcept { + index.first() = {}; + vertices.clear(); + deps.clear(); + sync_on = {}; + } + + /** + * @brief Exchanges the contents with those of a given flow builder. + * @param other Flow builder to exchange the content with. + */ + void swap(basic_flow &other) { + using std::swap; + std::swap(index, other.index); + std::swap(vertices, other.vertices); + std::swap(deps, other.deps); + std::swap(sync_on, other.sync_on); + } + + /** + * @brief Returns the number of tasks. + * @return The number of tasks. + */ + [[nodiscard]] size_type size() const noexcept { + return vertices.size(); + } + + /** + * @brief Binds a task to a flow builder. + * @param value Task identifier. + * @return This flow builder. + */ + basic_flow &bind(const id_type value) { + sync_on += (sync_on == vertices.size()); + const auto it = vertices.emplace(value).first; + index.first() = size_type(it - vertices.begin()); + return *this; + } + + /** + * @brief Turns the current task into a sync point. + * @return This flow builder. + */ + basic_flow &sync() { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + sync_on = index.first(); + + for(const auto &elem: deps) { + elem.second.emplace_back(sync_on, true); + } + + return *this; + } + + /** + * @brief Assigns a resource to the current task with a given access mode. + * @param res Resource identifier. + * @param is_rw Access mode. + * @return This flow builder. + */ + basic_flow &set(const id_type res, bool is_rw = false) { + emplace(res, is_rw); + return *this; + } + + /** + * @brief Assigns a read-only resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &ro(const id_type res) { + emplace(res, false); + return *this; + } + + /** + * @brief Assigns a range of read-only resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + ro(It first, It last) { + for(; first != last; ++first) { + emplace(*first, false); + } + + return *this; + } + + /** + * @brief Assigns a writable resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &rw(const id_type res) { + emplace(res, true); + return *this; + } + + /** + * @brief Assigns a range of writable resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + rw(It first, It last) { + for(; first != last; ++first) { + emplace(*first, true); + } + + return *this; + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency matrix of the task graph. + */ + [[nodiscard]] graph_type graph() const { + graph_type matrix{vertices.size(), get_allocator()}; + + setup_graph(matrix); + transitive_closure(matrix); + transitive_reduction(matrix); + + return matrix; + } + +private: + compressed_pair index; + task_container_type vertices; + deps_container_type deps; + size_type sync_on; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" + +// #include "storage.hpp" + +// #include "view.hpp" + + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Registry Basic registry type. + */ +template +class as_view { + template + auto dispatch(get_t, exclude_t) const { + return reg.template view...>(exclude_t...>{}); + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Get Type of storage used to construct the view. + * @tparam Exclude Types of storage used to filter the view. + * @return A newly created view. + */ + template + operator basic_view() const { + return dispatch(Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Converts a registry to a group. + * @tparam Registry Basic registry type. + */ +template +class as_group { + template + auto dispatch(owned_t, get_t, exclude_t) const { + if constexpr(std::is_const_v) { + return reg.template group_if_exists(get_t{}, exclude_t{}); + } else { + return reg.template group...>(get_t...>{}, exclude_t...>{}); + } + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Owned Types of _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + * @return A newly created group. + */ + template + operator basic_group() const { + return dispatch(Owned{}, Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @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 Registry Basic registry type. + * @param reg A registry that contains the given entity and its components. + * @param entt Entity from which to get the component. + */ +template>> +void invoke(Registry ®, const typename Registry::entity_type entt) { + static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); + delegate func; + func.template connect(reg.template get>(entt)); + func(reg, entt); +} + +/** + * @brief Returns the entity associated with a given component. + * + * @warning + * Currently, this function only works correctly with the default storage as it + * makes assumptions about how the components are laid out. + * + * @tparam Args Storage type template parameters. + * @param storage A storage that contains the given component. + * @param instance A valid component instance. + * @return The entity associated with the given component. + */ +template +auto to_entity(const basic_storage &storage, const typename basic_storage::value_type &instance) -> typename basic_storage::entity_type { + constexpr auto page_size = basic_storage::traits_type::page_size; + const typename basic_storage::base_type &base = storage; + const auto *addr = std::addressof(instance); + + for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) { + if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast(page_size)) { + return *(it + dist); + } + } + + return null; +} + +/** + * @copybrief to_entity + * @tparam Args Registry type template parameters. + * @tparam Component Type of component. + * @param reg A registry that contains the given entity and its components. + * @param instance A valid component instance. + * @return The entity associated with the given component. + */ +template +[[deprecated("use storage based to_entity instead")]] typename basic_registry::entity_type to_entity(const basic_registry ®, const Component &instance) { + if(const auto *storage = reg.template storage(); storage) { + return to_entity(*storage, instance); + } + + return null; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct sigh_helper; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + */ +template +struct sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + */ + sigh_helper(registry_type &ref) + : bucket{&ref} {} + + /** + * @brief Binds a properly initialized helper to a given signal type. + * @tparam Type Type of signal to bind the helper to. + * @param id Optional name for the underlying storage to use. + * @return A helper for a given registry and signal type. + */ + template + auto with(const id_type id = type_hash::value()) noexcept { + return sigh_helper{*bucket, id}; + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + [[nodiscard]] registry_type ®istry() noexcept { + return *bucket; + } + +private: + registry_type *bucket; +}; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + * @tparam Type Type of signal to connect listeners to. + */ +template +struct sigh_helper final: sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + * @param id Optional name for the underlying storage to use. + */ + sigh_helper(registry_type &ref, const id_type id = type_hash::value()) + : sigh_helper{ref}, + name{id} {} + + /** + * @brief Forwards the call to `on_construct` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_construct(Args &&...args) { + this->registry().template on_construct(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_update` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_update(Args &&...args) { + this->registry().template on_update(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_destroy` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_destroy(Args &&...args) { + this->registry().template on_destroy(name).template connect(std::forward(args)...); + return *this; + } + +private: + id_type name; +}; + +/** + * @brief Deduction guide. + * @tparam Registry Basic registry type. + */ +template +sigh_helper(Registry &) -> sigh_helper; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct is_view: std::false_type {}; + +template +struct is_view>: std::true_type {}; + +template +inline constexpr bool is_view_v = is_view::value; + +template +struct unpack_type { + using ro = std::conditional_t< + type_list_contains_v || (std::is_const_v && !type_list_contains_v>), + type_list>, + type_list<>>; + + using rw = std::conditional_t< + type_list_contains_v> || (!std::is_const_v && !type_list_contains_v), + type_list, + type_list<>>; +}; + +template +struct unpack_type, type_list> { + using ro = type_list<>; + using rw = type_list<>; +}; + +template +struct unpack_type, type_list> + : unpack_type, type_list> {}; + +template +struct unpack_type, exclude_t>, type_list> { + using ro = type_list_cat_t, typename unpack_type, type_list>::ro...>; + using rw = type_list_cat_t, type_list>::rw...>; +}; + +template +struct unpack_type, exclude_t>, type_list> + : unpack_type, exclude_t>, type_list> {}; + +template +struct resource_traits; + +template +struct resource_traits, type_list> { + using args = type_list...>; + using ro = type_list_cat_t>::ro..., typename unpack_type>::ro...>; + using rw = type_list_cat_t>::rw..., typename unpack_type>::rw...>; +}; + +template +resource_traits...>, type_list> free_function_to_resource_traits(Ret (*)(Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (*)(Type &, Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const); + +} // namespace internal +/*! @endcond */ + +/** + * @brief Utility class for creating a static task graph. + * + * This class offers minimal support (but sufficient in many cases) for creating + * an execution graph from functions and their requirements on resources.
+ * Note that the resulting tasks aren't executed in any case. This isn't the + * goal of the tool. Instead, they are returned to the user in the form of a + * graph that allows for safe execution. + * + * @tparam Registry Basic registry type. + */ +template +class basic_organizer final { + using callback_type = void(const void *, Registry &); + using prepare_type = void(Registry &); + using dependency_type = std::size_t(const bool, const type_info **, const std::size_t); + + struct vertex_data final { + std::size_t ro_count{}; + std::size_t rw_count{}; + const char *name{}; + const void *payload{}; + callback_type *callback{}; + dependency_type *dependency; + prepare_type *prepare{}; + const type_info *info{}; + }; + + template + [[nodiscard]] static decltype(auto) extract(Registry ®) { + if constexpr(std::is_same_v) { + return reg; + } else if constexpr(internal::is_view_v) { + return static_cast(as_view{reg}); + } else { + return reg.ctx().template emplace>(); + } + } + + template + [[nodiscard]] static auto to_args(Registry ®, type_list) { + return std::tuple(reg))...>(extract(reg)...); + } + + template + static std::size_t fill_dependencies(type_list, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) { + if constexpr(sizeof...(Type) == 0u) { + return {}; + } else { + const type_info *info[sizeof...(Type)]{&type_id()...}; + const auto length = count < sizeof...(Type) ? count : sizeof...(Type); + + for(std::size_t pos{}; pos < length; ++pos) { + buffer[pos] = info[pos]; + } + + return length; + } + } + + template + void track_dependencies(std::size_t index, const bool requires_registry, type_list, type_list) { + builder.bind(static_cast(index)); + builder.set(type_hash::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u)); + (builder.ro(type_hash::value()), ...); + (builder.rw(type_hash::value()), ...); + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Raw task function type. */ + using function_type = callback_type; + + /*! @brief Vertex type of a task graph defined as an adjacency list. */ + struct vertex { + /** + * @brief Constructs a vertex of the task graph. + * @param vtype True if the vertex is a top-level one, false otherwise. + * @param data The data associated with the vertex. + * @param edges The indices of the children in the adjacency list. + */ + vertex(const bool vtype, vertex_data data, std::vector edges) + : is_top_level{vtype}, + node{std::move(data)}, + reachable{std::move(edges)} {} + + /** + * @brief Fills a buffer with the type info objects for the writable + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept { + return node.dependency(false, buffer, length); + } + + /** + * @brief Fills a buffer with the type info objects for the read-only + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept { + return node.dependency(true, buffer, length); + } + + /** + * @brief Returns the number of read-only resources of a vertex. + * @return The number of read-only resources of the vertex. + */ + size_type ro_count() const noexcept { + return node.ro_count; + } + + /** + * @brief Returns the number of writable resources of a vertex. + * @return The number of writable resources of the vertex. + */ + size_type rw_count() const noexcept { + return node.rw_count; + } + + /** + * @brief Checks if a vertex is also a top-level one. + * @return True if the vertex is a top-level one, false otherwise. + */ + bool top_level() const noexcept { + return is_top_level; + } + + /** + * @brief Returns a type info object associated with a vertex. + * @return A properly initialized type info object. + */ + const type_info &info() const noexcept { + return *node.info; + } + + /** + * @brief Returns a user defined name associated with a vertex, if any. + * @return The user defined name associated with the vertex, if any. + */ + const char *name() const noexcept { + return node.name; + } + + /** + * @brief Returns the function associated with a vertex. + * @return The function associated with the vertex. + */ + function_type *callback() const noexcept { + return node.callback; + } + + /** + * @brief Returns the payload associated with a vertex, if any. + * @return The payload associated with the vertex, if any. + */ + const void *data() const noexcept { + return node.payload; + } + + /** + * @brief Returns the list of nodes reachable from a given vertex. + * @return The list of nodes reachable from the vertex. + */ + const std::vector &children() const noexcept { + return reachable; + } + + /** + * @brief Prepares a registry and assures that all required resources + * are properly instantiated before using them. + * @param reg A valid registry. + */ + void prepare(registry_type ®) const { + node.prepare ? node.prepare(reg) : void(); + } + + private: + bool is_top_level; + vertex_data node; + std::vector reachable; + }; + + /** + * @brief Adds a free function to the task list. + * @tparam Candidate Function to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param name Optional name to associate with the task. + */ + template + void emplace(const char *name = nullptr) { + using resource_type = decltype(internal::free_function_to_resource_traits(Candidate)); + constexpr auto requires_registry = type_list_contains_v; + + callback_type *callback = +[](const void *, registry_type ®) { + std::apply(Candidate, to_args(reg, typename resource_type::args{})); + }; + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + nullptr, + callback, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, + &type_id>()}; + + track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Adds a free function with payload or a member function with an + * instance to the task list. + * @tparam Candidate Function or member to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @param name Optional name to associate with the task. + */ + template + void emplace(Type &value_or_instance, const char *name = nullptr) { + using resource_type = decltype(internal::constrained_function_to_resource_traits(Candidate)); + constexpr auto requires_registry = type_list_contains_v; + + callback_type *callback = +[](const void *payload, registry_type ®) { + Type *curr = static_cast(const_cast *>(payload)); + std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{}))); + }; + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + &value_or_instance, + callback, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, + &type_id>()}; + + track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Adds an user defined function with optional payload to the task + * list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param func Function to add to the task list. + * @param payload User defined arbitrary data. + * @param name Optional name to associate with the task. + */ + template + void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) { + using resource_type = internal::resource_traits, type_list>; + track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{}); + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + payload, + func, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + nullptr, + &type_id()}; + + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency list of the task graph. + */ + std::vector graph() { + std::vector adjacency_list{}; + adjacency_list.reserve(vertices.size()); + auto adjacency_matrix = builder.graph(); + + for(auto curr: adjacency_matrix.vertices()) { + const auto iterable = adjacency_matrix.in_edges(curr); + std::vector reachable{}; + + for(auto &&edge: adjacency_matrix.out_edges(curr)) { + reachable.push_back(edge.second); + } + + adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable)); + } + + return adjacency_list; + } + + /*! @brief Erases all elements from a container. */ + void clear() { + builder.clear(); + vertices.clear(); + } + +private: + std::vector vertices; + flow builder; +}; + +} // namespace entt + +#endif + +// #include "entity/registry.hpp" +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../core/algorithm.hpp" + +// #include "../core/any.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" + +// #include "mixin.hpp" +#ifndef ENTT_ENTITY_MIXIN_HPP +#define ENTT_ENTITY_MIXIN_HPP + +#include +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../signal/sigh.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Mixin type used to add signal support to storage types. + * + * The function type of a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * This applies to all signals made available. + * + * @tparam Type Underlying storage type. + * @tparam Registry Basic registry type. + */ +template +class basic_sigh_mixin final: public Type { + using underlying_type = Type; + using owner_type = Registry; + + using basic_registry_type = basic_registry; + using sigh_type = sigh; + using underlying_iterator = typename underlying_type::base_type::basic_iterator; + + static_assert(std::is_base_of_v, "Invalid registry type"); + + owner_type &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast(*owner); + } + + void pop(underlying_iterator first, underlying_iterator last) final { + if(auto ® = owner_or_assert(); destruction.empty()) { + underlying_type::pop(first, last); + } else { + for(; first != last; ++first) { + const auto entt = *first; + destruction.publish(reg, entt); + const auto it = underlying_type::find(entt); + underlying_type::pop(it, it + 1u); + } + } + } + + void pop_all() final { + if(auto ® = owner_or_assert(); !destruction.empty()) { + for(auto it = underlying_type::base_type::begin(0), last = underlying_type::base_type::end(0); it != last; ++it) { + if constexpr(std::is_same_v) { + destruction.publish(reg, *it); + } else { + if constexpr(underlying_type::traits_type::in_place_delete) { + if(const auto entt = *it; entt != tombstone) { + destruction.publish(reg, entt); + } + } else { + destruction.publish(reg, *it); + } + } + } + } + + underlying_type::pop_all(); + } + + underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final { + const auto it = underlying_type::try_emplace(entt, force_back, value); + + if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) { + construction.publish(reg, *it); + } + + return it; + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = owner_type; + + /*! @brief Default constructor. */ + basic_sigh_mixin() + : basic_sigh_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sigh_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{}, + construction{allocator}, + destruction{allocator}, + update{allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sigh_mixin(basic_sigh_mixin &&other) noexcept + : underlying_type{std::move(other)}, + owner{other.owner}, + construction{std::move(other.construction)}, + destruction{std::move(other.destruction)}, + update{std::move(other.update)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) noexcept + : underlying_type{std::move(other), allocator}, + owner{other.owner}, + construction{std::move(other.construction), allocator}, + destruction{std::move(other.destruction), allocator}, + update{std::move(other.update), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept { + underlying_type::operator=(std::move(other)); + owner = other.owner; + construction = std::move(other.construction); + destruction = std::move(other.destruction); + update = std::move(other.update); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_sigh_mixin &other) { + using std::swap; + underlying_type::swap(other); + swap(owner, other.owner); + swap(construction, other.construction); + swap(destruction, other.destruction); + swap(update, other.update); + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.
+ * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() noexcept { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() noexcept { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() noexcept { + return sink{destruction}; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @return A return value as returned by the underlying storage. + */ + auto emplace() { + const auto entt = underlying_type::emplace(); + construction.publish(owner_or_assert(), entt); + return entt; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam Args Types of arguments to forward to the underlying storage. + * @param hint A valid identifier. + * @param args Parameters to forward to the underlying storage. + * @return A return value as returned by the underlying storage. + */ + template + decltype(auto) emplace(const entity_type hint, Args &&...args) { + if constexpr(std::is_same_v) { + const auto entt = underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), entt); + return entt; + } else { + underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), hint); + return this->get(hint); + } + } + + /** + * @brief Patches the given instance for an entity. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template + decltype(auto) patch(const entity_type entt, Func &&...func) { + underlying_type::patch(entt, std::forward(func)...); + update.publish(owner_or_assert(), entt); + return this->get(entt); + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam It Iterator type (as required by the underlying storage type). + * @tparam Args Types of arguments to forward to the underlying storage. + * @param first An iterator to the first element of the range. + * @param last An iterator past the last element of the range. + * @param args Parameters to use to forward to the underlying storage. + */ + template + void insert(It first, It last, Args &&...args) { + auto from = underlying_type::size(); + underlying_type::insert(first, last, std::forward(args)...); + + if(auto ® = owner_or_assert(); !construction.empty()) { + for(const auto to = underlying_type::size(); from != to; ++from) { + construction.publish(reg, underlying_type::operator[](from)); + } + } + } + + /** + * @brief Forwards variables to derived classes, if any. + * @param value A variable wrapped in an opaque container. + */ + void bind(any value) noexcept final { + auto *reg = any_cast(&value); + owner = reg ? reg : owner; + underlying_type::bind(std::move(value)); + } + +private: + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; +}; + +} // namespace entt + +#endif + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "view.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class registry_storage_iterator final { + template + friend class registry_storage_iterator; + + using mapped_type = std::remove_reference_t()->second)>; + +public: + using value_type = std::pair &>; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr registry_storage_iterator() noexcept + : it{} {} + + constexpr registry_storage_iterator(It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr registry_storage_iterator(const registry_storage_iterator &other) noexcept + : registry_storage_iterator{other.it} {} + + constexpr registry_storage_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr registry_storage_iterator operator++(int) noexcept { + registry_storage_iterator orig = *this; + return ++(*this), orig; + } + + constexpr registry_storage_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr registry_storage_iterator operator--(int) noexcept { + registry_storage_iterator orig = *this; + return operator--(), orig; + } + + constexpr registry_storage_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr registry_storage_iterator operator+(const difference_type value) const noexcept { + registry_storage_iterator copy = *this; + return (copy += value); + } + + constexpr registry_storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr registry_storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, *it[value].second}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->first, *it->second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template + friend constexpr std::ptrdiff_t operator-(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + + template + friend constexpr bool operator==(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + + template + friend constexpr bool operator<(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class registry_context { + using alloc_traits = std::allocator_traits; + using allocator_type = typename alloc_traits::template rebind_alloc>>; + +public: + explicit registry_context(const allocator_type &allocator) + : ctx{allocator} {} + + template + Type &emplace_as(const id_type id, Args &&...args) { + return any_cast(ctx.try_emplace(id, std::in_place_type, std::forward(args)...).first->second); + } + + template + Type &emplace(Args &&...args) { + return emplace_as(type_id().hash(), std::forward(args)...); + } + + template + Type &insert_or_assign(const id_type id, Type &&value) { + return any_cast> &>(ctx.insert_or_assign(id, std::forward(value)).first->second); + } + + template + Type &insert_or_assign(Type &&value) { + return insert_or_assign(type_id().hash(), std::forward(value)); + } + + template + bool erase(const id_type id = type_id().hash()) { + const auto it = ctx.find(id); + return it != ctx.end() && it->second.type() == type_id() ? (ctx.erase(it), true) : false; + } + + template + [[nodiscard]] const Type &get(const id_type id = type_id().hash()) const { + return any_cast(ctx.at(id)); + } + + template + [[nodiscard]] Type &get(const id_type id = type_id().hash()) { + return any_cast(ctx.at(id)); + } + + template + [[nodiscard]] const Type *find(const id_type id = type_id().hash()) const { + const auto it = ctx.find(id); + return it != ctx.cend() ? any_cast(&it->second) : nullptr; + } + + template + [[nodiscard]] Type *find(const id_type id = type_id().hash()) { + const auto it = ctx.find(id); + return it != ctx.end() ? any_cast(&it->second) : nullptr; + } + + template + [[nodiscard]] bool contains(const id_type id = type_id().hash()) const { + const auto it = ctx.find(id); + return it != ctx.cend() && it->second.type() == type_id(); + } + +private: + dense_map, identity, std::equal_to, allocator_type> ctx; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Fast and reliable entity-component system. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_registry { + using base_type = basic_sparse_set; + + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + + // std::shared_ptr because of its type erased allocator which is useful here + using pool_container_type = dense_map, identity, std::equal_to, typename alloc_traits::template rebind_alloc>>>; + using group_container_type = dense_map, identity, std::equal_to, typename alloc_traits::template rebind_alloc>>>; + + template + [[nodiscard]] auto &assure([[maybe_unused]] const id_type id = type_hash::value()) { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + + if constexpr(std::is_same_v) { + return entities; + } else { + auto &cpool = pools[id]; + + if(!cpool) { + using storage_type = storage_for_type; + using alloc_type = typename storage_type::allocator_type; + + if constexpr(std::is_void_v && !std::is_constructible_v) { + // std::allocator has no cross constructors (waiting for C++20) + cpool = std::allocate_shared(get_allocator(), alloc_type{}); + } else { + cpool = std::allocate_shared(get_allocator(), get_allocator()); + } + + cpool->bind(forward_as_any(*this)); + } + + ENTT_ASSERT(cpool->type() == type_id(), "Unexpected type"); + return static_cast &>(*cpool); + } + } + + template + [[nodiscard]] const auto *assure([[maybe_unused]] const id_type id = type_hash::value()) const { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + + if constexpr(std::is_same_v) { + return &entities; + } else { + if(const auto it = pools.find(id); it != pools.cend()) { + ENTT_ASSERT(it->second->type() == type_id(), "Unexpected type"); + return static_cast *>(it->second.get()); + } + + return static_cast *>(nullptr); + } + } + + void rebind() { + entities.bind(forward_as_any(*this)); + + for(auto &&curr: pools) { + curr.second->bind(forward_as_any(*this)); + } + } + +public: + /*! @brief Entity traits. */ + using traits_type = typename base_type::traits_type; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Context type. */ + using context = internal::registry_context; + /*! @brief Iterable registry type. */ + using iterable = iterable_adaptor>; + /*! @brief Constant iterable registry type. */ + using const_iterable = iterable_adaptor>; + + /** + * @copybrief storage_for + * @tparam Type Storage value type, eventually const. + */ + template + using storage_for_type = typename storage_for>>::type; + + /*! @brief Default constructor. */ + basic_registry() + : basic_registry{allocator_type{}} {} + + /** + * @brief Constructs an empty registry with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_registry(const allocator_type &allocator) + : basic_registry{0u, allocator} {} + + /** + * @brief Allocates enough memory upon construction to store `count` pools. + * @param count The number of pools to allocate memory for. + * @param allocator The allocator to use. + */ + basic_registry(const size_type count, const allocator_type &allocator = allocator_type{}) + : vars{allocator}, + pools{allocator}, + groups{allocator}, + entities{allocator} { + pools.reserve(count); + rebind(); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_registry(basic_registry &&other) noexcept + : vars{std::move(other.vars)}, + pools{std::move(other.pools)}, + groups{std::move(other.groups)}, + entities{std::move(other.entities)} { + rebind(); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This registry. + */ + basic_registry &operator=(basic_registry &&other) noexcept { + vars = std::move(other.vars); + pools = std::move(other.pools); + groups = std::move(other.groups); + entities = std::move(other.entities); + + rebind(); + + return *this; + } + + /** + * @brief Exchanges the contents with those of a given registry. + * @param other Registry to exchange the content with. + */ + void swap(basic_registry &other) { + using std::swap; + + swap(vars, other.vars); + swap(pools, other.pools); + swap(groups, other.groups); + swap(entities, other.entities); + + rebind(); + other.rebind(); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return entities.get_allocator(); + } + + /** + * @brief Returns an iterable object to use to _visit_ a registry. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage. + * + * @return An iterable object to use to _visit_ the registry. + */ + [[nodiscard]] iterable storage() noexcept { + return iterable_adaptor{internal::registry_storage_iterator{pools.begin()}, internal::registry_storage_iterator{pools.end()}}; + } + + /*! @copydoc storage */ + [[nodiscard]] const_iterable storage() const noexcept { + return iterable_adaptor{internal::registry_storage_iterator{pools.cbegin()}, internal::registry_storage_iterator{pools.cend()}}; + } + + /** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return A pointer to the storage if it exists, a null pointer otherwise. + */ + [[nodiscard]] common_type *storage(const id_type id) { + return const_cast(std::as_const(*this).storage(id)); + } + + /** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return A pointer to the storage if it exists, a null pointer otherwise. + */ + [[nodiscard]] const common_type *storage(const id_type id) const { + const auto it = pools.find(id); + return it == pools.cend() ? nullptr : it->second.get(); + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Type Type of component of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given component type. + */ + template + storage_for_type &storage(const id_type id = type_hash::value()) { + return assure(id); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given component type. + */ + template + const storage_for_type *storage(const id_type id = type_hash::value()) const { + return assure(id); + } + + /** + * @brief Checks if an identifier refers to a valid entity. + * @param entt An identifier, either valid or not. + * @return True if the identifier is valid, false otherwise. + */ + [[nodiscard]] bool valid(const entity_type entt) const { + return entities.contains(entt) && (entities.index(entt) < entities.free_list()); + } + + /** + * @brief Returns the actual version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if valid, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entt) const { + return entities.current(entt); + } + + /** + * @brief Creates a new entity or recycles a destroyed one. + * @return A valid identifier. + */ + [[nodiscard]] entity_type create() { + return entities.emplace(); + } + + /** + * @copybrief create + * + * If the requested entity isn't in use, the suggested identifier is used. + * Otherwise, a new identifier is generated. + * + * @param hint Required identifier. + * @return A valid identifier. + */ + [[nodiscard]] entity_type create(const entity_type hint) { + return entities.emplace(hint); + } + + /** + * @brief Assigns each element in a range an identifier. + * + * @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) { + entities.insert(std::move(first), std::move(last)); + } + + /** + * @brief Destroys an entity and releases its identifier. + * + * @warning + * Adding or removing components to an entity that is being destroyed can + * result in undefined behavior. + * + * @param entt A valid identifier. + * @return The version of the recycled entity. + */ + version_type destroy(const entity_type entt) { + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[pos - 1u].second->remove(entt); + } + + entities.erase(entt); + return entities.current(entt); + } + + /** + * @brief Destroys an entity and releases its identifier. + * + * The suggested version or the valid version closest to the suggested one + * is used instead of the implicitly generated version. + * + * @sa destroy + * + * @param entt A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ + version_type destroy(const entity_type entt, const version_type version) { + destroy(entt); + const auto elem = traits_type::construct(traits_type::to_entity(entt), version); + return entities.bump((elem == tombstone) ? traits_type::next(elem) : elem); + } + + /** + * @brief Destroys all entities in a range and releases their identifiers. + * + * @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) { + entities.sort_as(first, last); + + const auto from = entities.cbegin(0); + const auto to = from + std::distance(first, last); + + for(auto &&curr: pools) { + curr.second->remove(from, to); + } + + entities.erase(from, to); + } + + /** + * @brief Assigns the given component to an entity. + * + * The component must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to assign a component to an entity that already owns it + * results in undefined behavior. + * + * @tparam Type Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param entt A valid 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 entt, Args &&...args) { + return assure().emplace(entt, std::forward(args)...); + } + + /** + * @brief Assigns each entity in a range the given component. + * + * @sa emplace + * + * @tparam Type 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 Type &value = {}) { + assure().insert(std::move(first), std::move(last), value); + } + + /** + * @brief Assigns each entity in a range the given components. + * + * @sa emplace + * + * @tparam Type 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. + */ + template::value_type, Type>>> + void insert(EIt first, EIt last, CIt from) { + assure().insert(first, last, from); + } + + /** + * @brief Assigns or replaces the given component for an entity. + * + * @sa emplace + * @sa replace + * + * @tparam Type Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entt A valid 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 entt, Args &&...args) { + if(auto &cpool = assure(); cpool.contains(entt)) { + return cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward(args)...}), ...); }); + } else { + return cpool.emplace(entt, std::forward(args)...); + } + } + + /** + * @brief Patches the given component for an entity. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * @warning + * Attempting to patch a component of an entity that doesn't own it + * results in undefined behavior. + * + * @tparam Type Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(const entity_type entt, Func &&...func) { + return assure().patch(entt, std::forward(func)...); + } + + /** + * @brief Replaces the given component for an entity. + * + * The component must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to replace a component of an entity that doesn't own it + * results in undefined behavior. + * + * @tparam Type Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ + template + decltype(auto) replace(const entity_type entt, Args &&...args) { + return patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward(args)...}), ...); }); + } + + /** + * @brief Removes the given components from an entity. + * @tparam Type Type of component to remove. + * @tparam Other Other types of components to remove. + * @param entt A valid identifier. + * @return The number of components actually removed. + */ + template + size_type remove(const entity_type entt) { + return (assure().remove(entt) + ... + assure().remove(entt)); + } + + /** + * @brief Removes the given components from all the entities in a range. + * + * @sa remove + * + * @tparam Type Type of component to remove. + * @tparam Other Other 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. + * @return The number of components actually removed. + */ + template + size_type remove(It first, It last) { + size_type count{}; + + if constexpr(std::is_same_v) { + common_type *cpools[sizeof...(Other) + 1u]{&assure(), &assure()...}; + + for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) { + if constexpr(sizeof...(Other) != 0u) { + if(cpools[pos]->data() == first.data()) { + std::swap(cpools[pos], cpools[sizeof...(Other)]); + } + } + + count += cpools[pos]->remove(first, last); + } + } else { + for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { + count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools); + } + } + + return count; + } + + /** + * @brief Erases the given components from an entity. + * + * @warning + * Attempting to erase a component from an entity that doesn't own it + * results in undefined behavior. + * + * @tparam Type Types of components to erase. + * @tparam Other Other types of components to erase. + * @param entt A valid identifier. + */ + template + void erase(const entity_type entt) { + (assure().erase(entt), (assure().erase(entt), ...)); + } + + /** + * @brief Erases the given components from all the entities in a range. + * + * @sa erase + * + * @tparam Type Types of components to erase. + * @tparam Other Other types of components to erase. + * @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 erase(It first, It last) { + if constexpr(std::is_same_v) { + common_type *cpools[sizeof...(Other) + 1u]{&assure(), &assure()...}; + + for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) { + if constexpr(sizeof...(Other) != 0u) { + if(cpools[pos]->data() == first.data()) { + std::swap(cpools[pos], cpools[sizeof...(Other)]); + } + } + + cpools[pos]->erase(first, last); + } + } else { + for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { + std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools); + } + } + } + + /** + * @brief Erases components satisfying specific criteria from an entity. + * + * The function type is equivalent to: + * + * @code{.cpp} + * void(const id_type, typename basic_registry::base_type &); + * @endcode + * + * Only storage where the entity exists are passed to the function. + * + * @tparam Func Type of the function object to invoke. + * @param entt A valid identifier. + * @param func A valid function object. + */ + template + void erase_if(const entity_type entt, Func func) { + for(auto [id, cpool]: storage()) { + if(cpool.contains(entt) && func(id, std::as_const(cpool))) { + cpool.erase(entt); + } + } + } + + /** + * @brief Removes all tombstones from a registry or only the pools for the + * given components. + * @tparam Type Types of components for which to clear all tombstones. + */ + template + void compact() { + if constexpr(sizeof...(Type) == 0u) { + for(auto &&curr: pools) { + curr.second->compact(); + } + } else { + (assure().compact(), ...); + } + } + + /** + * @brief Check if an entity is part of all the given storage. + * @tparam Type Type of storage to check for. + * @param entt A valid identifier. + * @return True if the entity is part of all the storage, false otherwise. + */ + template + [[nodiscard]] bool all_of([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + auto *cpool = assure...>(); + return cpool && cpool->contains(entt); + } else { + return (all_of(entt) && ...); + } + } + + /** + * @brief Check if an entity is part of at least one given storage. + * @tparam Type Type of storage to check for. + * @param entt A valid identifier. + * @return True if the entity is part of at least one storage, false + * otherwise. + */ + template + [[nodiscard]] bool any_of([[maybe_unused]] const entity_type entt) const { + return (all_of(entt) || ...); + } + + /** + * @brief Returns references to the given components for an entity. + * + * @warning + * Attempting to get a component from an entity that doesn't own it results + * in undefined behavior. + * + * @tparam Type Types of components to get. + * @param entt A valid identifier. + * @return References to the components owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + return (assure>()->get(entt), ...); + } else { + return std::forward_as_tuple(get(entt)...); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) { + if constexpr(sizeof...(Type) == 1u) { + return (static_cast &>(assure>()).get(entt), ...); + } else { + return std::forward_as_tuple(get(entt)...); + } + } + + /** + * @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. + * + * @sa get + * @sa emplace + * + * @tparam Type Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entt, Args &&...args) { + if(auto &cpool = assure(); cpool.contains(entt)) { + return cpool.get(entt); + } else { + return cpool.emplace(entt, std::forward(args)...); + } + } + + /** + * @brief Returns pointers to the given components for an entity. + * + * @note + * The registry retains ownership of the pointed-to components. + * + * @tparam Type Types of components to get. + * @param entt A valid identifier. + * @return Pointers to the components owned by the entity. + */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + const auto *cpool = assure...>(); + return (cpool && cpool->contains(entt)) ? std::addressof(cpool->get(entt)) : nullptr; + } else { + return std::make_tuple(try_get(entt)...); + } + } + + /*! @copydoc try_get */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) { + if constexpr(sizeof...(Type) == 1u) { + return (const_cast(std::as_const(*this).template try_get(entt)), ...); + } else { + return std::make_tuple(try_get(entt)...); + } + } + + /** + * @brief Clears a whole registry or the pools for the given components. + * @tparam Type Types of components to remove from their entities. + */ + template + void clear() { + if constexpr(sizeof...(Type) == 0u) { + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[pos - 1u].second->clear(); + } + + const auto elem = entities.each(); + entities.erase(elem.begin().base(), elem.end().base()); + } else { + (assure().clear(), ...); + } + } + + /** + * @brief Checks if an entity has components assigned. + * @param entt A valid identifier. + * @return True if the entity has no components assigned, false otherwise. + */ + [[nodiscard]] bool orphan(const entity_type entt) const { + return std::none_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return curr.second->contains(entt); }); + } + + /** + * @brief Returns a sink object for the given component. + * + * Use this function 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(basic_registry &, Entity); + * @endcode + * + * Listeners are invoked **after** assigning the component to the entity. + * + * @sa sink + * + * @tparam Type Type of component of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_construct(const id_type id = type_hash::value()) { + return assure(id).on_construct(); + } + + /** + * @brief Returns a sink object for the given component. + * + * Use this function 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(basic_registry &, Entity); + * @endcode + * + * Listeners are invoked **after** updating the component. + * + * @sa sink + * + * @tparam Type Type of component of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_update(const id_type id = type_hash::value()) { + return assure(id).on_update(); + } + + /** + * @brief Returns a sink object for the given component. + * + * Use this function 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(basic_registry &, Entity); + * @endcode + * + * Listeners are invoked **before** removing the component from the entity. + * + * @sa sink + * + * @tparam Type Type of component of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_destroy(const id_type id = type_hash::value()) { + return assure(id).on_destroy(); + } + + /** + * @brief Returns a view for the given components. + * @tparam Type Type of component used to construct the view. + * @tparam Other Other types of components used to construct the view. + * @tparam Exclude Types of components used to filter the view. + * @return A newly created view. + */ + template + [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> + view(exclude_t = exclude_t{}) const { + const auto cpools = std::make_tuple(assure>(), assure>()..., assure>()...); + basic_view, storage_for_type...>, exclude_t...>> elem{}; + std::apply([&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }, cpools); + return elem; + } + + /*! @copydoc view */ + template + [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> + view(exclude_t = exclude_t{}) { + return {assure>(), assure>()..., assure>()...}; + } + + /** + * @brief Returns a group for the given components. + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group, if any. + * @tparam Exclude Types of storage used to filter the group, if any. + * @return A newly created group. + */ + template + basic_group...>, get_t...>, exclude_t...>> + group(get_t = get_t{}, exclude_t = exclude_t{}) { + using handler_type = typename basic_group...>, get_t...>, exclude_t...>>::handler; + + if(auto it = groups.find(type_hash::value()); it != groups.cend()) { + return {*std::static_pointer_cast(it->second)}; + } + + std::shared_ptr handler{}; + + if constexpr(sizeof...(Owned) == 0u) { + handler = std::allocate_shared(get_allocator(), get_allocator(), assure>()..., assure>()...); + } else { + handler = std::allocate_shared(get_allocator(), assure>()..., assure>()..., assure>()...); + [[maybe_unused]] const id_type elem[]{type_hash>::value()..., type_hash>::value()..., type_hash>::value()...}; + ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [&elem](const auto &data) { return data.second->owned(elem, sizeof...(Owned)) == 0u; }), "Conflicting groups"); + } + + groups.emplace(type_hash::value(), handler); + return {*handler}; + } + + /*! @copydoc group */ + template + basic_group...>, get_t...>, exclude_t...>> + group_if_exists(get_t = get_t{}, exclude_t = exclude_t{}) const { + using handler_type = typename basic_group...>, get_t...>, exclude_t...>>::handler; + + if(auto it = groups.find(type_hash::value()); it != groups.cend()) { + return {*std::static_pointer_cast(it->second)}; + } + + return {}; + } + + /** + * @brief Checks whether the given components belong to any group. + * @tparam Type Type of component in which one is interested. + * @tparam Other Other types of components in which one is interested. + * @return True if the pools of the given components are _free_, false + * otherwise. + */ + template + [[nodiscard]] bool owned() const { + const id_type elem[]{type_hash>::value(), type_hash>::value()...}; + return std::any_of(groups.cbegin(), groups.cend(), [&elem](auto &&data) { return data.second->owned(elem, 1u + sizeof...(Other)); }); + } + + /** + * @brief Sorts the elements of a given component. + * + * The comparison function object returns `true` if the first element is + * _less_ than the second one, `false` otherwise. Its signature is also + * equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Type &, const Type &); + * @endcode + * + * Moreover, it shall induce a _strict weak ordering_ on the values.
+ * The sort function object offers an `operator()` that accepts: + * + * * 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 object to use to compare the elements. + * + * The comparison 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. + * + * @tparam Type 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) { + ENTT_ASSERT(!owned(), "Cannot sort owned storage"); + auto &cpool = assure(); + + if constexpr(std::is_invocable_v) { + auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); }; + cpool.sort(std::move(comp), std::move(algo), std::forward(args)...); + } else { + cpool.sort(std::move(compare), std::move(algo), std::forward(args)...); + } + } + + /** + * @brief Sorts two pools of components in the same way. + * + * Entities and components in `To` which are part of both storage are sorted + * internally with the order they have in `From`. The others follow in no + * particular order. + * + * @warning + * Pools of components owned by a group cannot be sorted. + * + * @tparam To Type of components to sort. + * @tparam From Type of components to use to sort. + */ + template + void sort() { + ENTT_ASSERT(!owned(), "Cannot sort owned storage"); + const base_type &cpool = assure(); + assure().sort_as(cpool.begin(), cpool.end()); + } + + /** + * @brief Returns the context object, that is, a general purpose container. + * @return The context object, that is, a general purpose container. + */ + context &ctx() noexcept { + return vars; + } + + /*! @copydoc ctx */ + const context &ctx() const noexcept { + return vars; + } + +private: + context vars; + pool_container_type pools; + group_container_type groups; + storage_for_type entities; +}; + +} // namespace entt + +#endif + +// #include "entity/runtime_view.hpp" +#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP +#define ENTT_ENTITY_RUNTIME_VIEW_HPP + +#include +#include +#include +#include +#include +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class runtime_view_iterator final { + using iterator_type = typename Set::iterator; + + [[nodiscard]] bool valid() const { + return (!tombstone_check || *it != tombstone) + && std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); }) + && std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); }); + } + +public: + using difference_type = typename iterator_type::difference_type; + using value_type = typename iterator_type::value_type; + using pointer = typename iterator_type::pointer; + using reference = typename iterator_type::reference; + using iterator_category = std::bidirectional_iterator_tag; + + constexpr runtime_view_iterator() noexcept + : pools{}, + filter{}, + it{}, + tombstone_check{} {} + + runtime_view_iterator(const std::vector &cpools, const std::vector &ignore, iterator_type curr) noexcept + : pools{&cpools}, + filter{&ignore}, + it{curr}, + tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} { + if(it != (*pools)[0]->end() && !valid()) { + ++(*this); + } + } + + runtime_view_iterator &operator++() { + while(++it != (*pools)[0]->end() && !valid()) {} + return *this; + } + + runtime_view_iterator operator++(int) { + runtime_view_iterator orig = *this; + return ++(*this), orig; + } + + runtime_view_iterator &operator--() { + while(--it != (*pools)[0]->begin() && !valid()) {} + return *this; + } + + runtime_view_iterator operator--(int) { + runtime_view_iterator orig = *this; + return operator--(), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return it.operator->(); + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr bool operator==(const runtime_view_iterator &other) const noexcept { + return it == other.it; + } + + [[nodiscard]] constexpr bool operator!=(const runtime_view_iterator &other) const noexcept { + return !(*this == other); + } + +private: + const std::vector *pools; + const std::vector *filter; + iterator_type it; + bool tombstone_check; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Generic runtime view. + * + * Runtime views iterate over those entities that are at least in the given + * storage. During initialization, a runtime view looks at the number of + * entities available for each component and uses the smallest set in order to + * get a performance boost when iterating. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the storage iterated by the view in any way + * invalidates all the iterators. + * + * @tparam Type Common base type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_runtime_view { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::runtime_view_iterator; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_runtime_view() noexcept + : basic_runtime_view{allocator_type{}} {} + + /** + * @brief Constructs an empty, invalid view with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_runtime_view(const allocator_type &allocator) + : pools{allocator}, + filter{allocator} {} + + /*! @brief Default copy constructor. */ + basic_runtime_view(const basic_runtime_view &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator) + : pools{other.pools, allocator}, + filter{other.filter, allocator} {} + + /*! @brief Default move constructor. */ + basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator) + : pools{std::move(other.pools), allocator}, + filter{std::move(other.filter), allocator} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + basic_runtime_view &operator=(const basic_runtime_view &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Exchanges the contents with those of a given view. + * @param other View to exchange the content with. + */ + void swap(basic_runtime_view &other) { + using std::swap; + swap(pools, other.pools); + swap(filter, other.filter); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pools.get_allocator(); + } + + /*! @brief Clears the view. */ + void clear() { + pools.clear(); + filter.clear(); + } + + /** + * @brief Appends an opaque storage object to a runtime view. + * @param base An opaque reference to a storage object. + * @return This runtime view. + */ + basic_runtime_view &iterate(common_type &base) { + if(pools.empty() || !(base.size() < pools[0u]->size())) { + pools.push_back(&base); + } else { + pools.push_back(std::exchange(pools[0u], &base)); + } + + return *this; + } + + /** + * @brief Adds an opaque storage object as a filter of a runtime view. + * @param base An opaque reference to a storage object. + * @return This runtime view. + */ + basic_runtime_view &exclude(common_type &base) { + filter.push_back(&base); + return *this; + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const { + return pools.empty() ? size_type{} : pools.front()->size(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity that has the given components. + */ + [[nodiscard]] iterator begin() const { + return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()}; + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * @return An iterator to the entity following the last entity that has the + * given components. + */ + [[nodiscard]] iterator end() const { + return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()}; + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return !pools.empty() + && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); }) + && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); }); + } + + /** + * @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.
+ * 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: + container_type pools; + container_type filter; +}; + +} // namespace entt + +#endif + +// #include "entity/snapshot.hpp" +#ifndef ENTT_ENTITY_SNAPSHOT_HPP +#define ENTT_ENTITY_SNAPSHOT_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "view.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +void orphans(Registry ®istry) { + auto &storage = registry.template storage(); + + for(auto entt: storage) { + if(registry.orphan(entt)) { + storage.erase(entt); + } + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 Registry Basic registry type. + */ +template +class basic_snapshot { + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = typename Registry::traits_type; + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot(const registry_type &source) noexcept + : reg{&source} {} + + /*! @brief Default move constructor. */ + basic_snapshot(basic_snapshot &&) noexcept = default; + + /*! @brief Default move assignment operator. @return This snapshot. */ + basic_snapshot &operator=(basic_snapshot &&) noexcept = default; + + /** + * @brief Serializes all elements of a type with associated identifiers. + * @tparam Type Type of elements to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @param id Optional name used to map the storage within the registry. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot &get(Archive &archive, const id_type id = type_hash::value()) const { + if(const auto *storage = reg->template storage(id); storage) { + archive(static_cast(storage->size())); + + if constexpr(std::is_same_v) { + archive(static_cast(storage->free_list())); + + for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) { + archive(*first); + } + } else if constexpr(component_traits::in_place_delete) { + const typename registry_type::common_type &base = *storage; + + for(auto it = base.rbegin(), last = base.rend(); it != last; ++it) { + if(const auto entt = *it; entt == tombstone) { + archive(static_cast(null)); + } else { + archive(entt); + std::apply([&archive](auto &&...args) { (archive(std::forward(args)), ...); }, storage->get_as_tuple(entt)); + } + } + } else { + for(auto elem: storage->reach()) { + std::apply([&archive](auto &&...args) { (archive(std::forward(args)), ...); }, elem); + } + } + } else { + archive(typename traits_type::entity_type{}); + } + + return *this; + } + + /** + * @brief Serializes all elements of a type with associated identifiers for + * the entities in a range. + * @tparam Type Type of elements 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. + * @param id Optional name used to map the storage within the registry. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot &get(Archive &archive, It first, It last, const id_type id = type_hash::value()) const { + static_assert(!std::is_same_v, "Entity types not supported"); + + if(const auto *storage = reg->template storage(id); storage && !storage->empty()) { + archive(static_cast(std::distance(first, last))); + + for(; first != last; ++first) { + if(const auto entt = *first; storage->contains(entt)) { + archive(entt); + std::apply([&archive](auto &&...args) { (archive(std::forward(args)), ...); }, storage->get_as_tuple(entt)); + } else { + archive(static_cast(null)); + } + } + } else { + archive(typename traits_type::entity_type{}); + } + + return *this; + } + +private: + const registry_type *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 Registry Basic registry type. + */ +template +class basic_snapshot_loader { + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = typename Registry::traits_type; + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot_loader(registry_type &source) noexcept + : reg{&source} { + // restoring a snapshot as a whole requires a clean registry + ENTT_ASSERT(reg->template storage().free_list() == 0u, "Registry must be empty"); + } + + /*! @brief Default move constructor. */ + basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default; + + /*! @brief Default move assignment operator. @return This loader. */ + basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default; + + /** + * @brief Restores all elements of a type with associated identifiers. + * @tparam Type Type of elements to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @param id Optional name used to map the storage within the registry. + * @return A valid loader to continue restoring data. + */ + template + basic_snapshot_loader &get(Archive &archive, const id_type id = type_hash::value()) { + auto &storage = reg->template storage(id); + typename traits_type::entity_type length{}; + + archive(length); + + if constexpr(std::is_same_v) { + typename traits_type::entity_type count{}; + + storage.reserve(length); + archive(count); + + for(entity_type entity = null; length; --length) { + archive(entity); + storage.emplace(entity); + } + + storage.free_list(count); + } else { + auto &other = reg->template storage(); + entity_type entt{null}; + + while(length--) { + if(archive(entt); entt != null) { + const auto entity = other.contains(entt) ? entt : other.emplace(entt); + ENTT_ASSERT(entity == entt, "Entity not available for use"); + + if constexpr(std::tuple_size_v == 0u) { + storage.emplace(entity); + } else { + Type elem{}; + archive(elem); + storage.emplace(entity, std::move(elem)); + } + } + } + } + + 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 function helps to identify and destroy those entities. + * + * @return A valid loader to continue restoring data. + */ + basic_snapshot_loader &orphans() { + internal::orphans(*reg); + return *this; + } + +private: + registry_type *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 application with + * the requirement of transferring somehow parts of the representation side to + * side. + * + * @tparam Registry Basic registry type. + */ +template +class basic_continuous_loader { + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = typename Registry::traits_type; + + void restore(typename Registry::entity_type entt) { + if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) { + if(!reg->valid(remloc[entity].second)) { + remloc[entity].second = reg->create(); + } + } else { + remloc.insert_or_assign(entity, std::make_pair(entt, reg->create())); + } + } + + 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, "Neither the key nor the value are of entity type"); + other.emplace(std::move(pair.first), map(pair.second)); + } + } + + using std::swap; + swap(container, other); + } + + template + auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) { + // vector like container + static_assert(std::is_same_v, "Invalid value type"); + + for(auto &&entt: container) { + entt = map(entt); + } + } + + template + void update([[maybe_unused]] Component &instance, [[maybe_unused]] Member Other::*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); + } + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_continuous_loader(registry_type &source) noexcept + : remloc{source.get_allocator()}, + 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 all elements of a type with associated identifiers. + * + * It creates local counterparts for remote elements as needed.
+ * Members are either data members of type entity_type or containers of + * entities. In both cases, a loader visits them and replaces entities with + * their local counterpart. + * + * @tparam Type Type of elements to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @param id Optional name used to map the storage within the registry. + * @return A valid loader to continue restoring data. + */ + template + basic_continuous_loader &get(Archive &archive, const id_type id = type_hash::value()) { + auto &storage = reg->template storage(id); + typename traits_type::entity_type length{}; + entity_type entt{null}; + + archive(length); + + if constexpr(std::is_same_v) { + typename traits_type::entity_type in_use{}; + + storage.reserve(length); + archive(in_use); + + for(std::size_t pos{}; pos < in_use; ++pos) { + archive(entt); + restore(entt); + } + + for(std::size_t pos = in_use; pos < length; ++pos) { + archive(entt); + + if(const auto entity = to_entity(entt); remloc.contains(entity)) { + if(reg->valid(remloc[entity].second)) { + reg->destroy(remloc[entity].second); + } + + remloc.erase(entity); + } + } + } else { + for(auto &&ref: remloc) { + storage.remove(ref.second.second); + } + + while(length--) { + if(archive(entt); entt != null) { + restore(entt); + + if constexpr(std::tuple_size_v == 0u) { + storage.emplace(map(entt)); + } else { + Type elem{}; + archive(elem); + storage.emplace(map(entt), std::move(elem)); + } + } + } + } + + 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 function helps to identify and destroy those entities. + * + * @return A non-const reference to this loader. + */ + basic_continuous_loader &orphans() { + internal::orphans(*reg); + return *this; + } + + /** + * @brief Tests if a loader knows about a given entity. + * @param entt A valid identifier. + * @return True if `entity` is managed by the loader, false otherwise. + */ + [[nodiscard]] bool contains(entity_type entt) const noexcept { + const auto it = remloc.find(to_entity(entt)); + return it != remloc.cend() && it->second.first == entt; + } + + /** + * @brief Returns the identifier to which an entity refers. + * @param entt A valid identifier. + * @return The local identifier if any, the null entity otherwise. + */ + [[nodiscard]] entity_type map(entity_type entt) const noexcept { + if(const auto it = remloc.find(to_entity(entt)); it != remloc.cend() && it->second.first == entt) { + return it->second.second; + } + + return null; + } + +private: + dense_map> remloc; + registry_type *reg; +}; + +} // namespace entt + +#endif + +// #include "entity/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 "../core/any.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct sparse_set_iterator final { + using value_type = typename Container::value_type; + using pointer = typename Container::const_pointer; + using reference = typename Container::const_reference; + using difference_type = typename Container::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr sparse_set_iterator() noexcept + : packed{}, + offset{} {} + + constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept + : packed{std::addressof(ref)}, + offset{idx} {} + + constexpr sparse_set_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr sparse_set_iterator operator++(int) noexcept { + sparse_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr sparse_set_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr sparse_set_iterator operator--(int) noexcept { + sparse_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr sparse_set_iterator operator+(const difference_type value) const noexcept { + sparse_set_iterator copy = *this; + return (copy += value); + } + + constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr sparse_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return packed->data()[index() - value]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return packed->data() + index(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer data() const noexcept { + return packed ? packed->data() : nullptr; + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + const Container *packed; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 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 + * Internal data structures arrange elements to maximize performance. 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. + * + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector; + using underlying_type = typename entt_traits::entity_type; + + [[nodiscard]] auto sparse_ptr(const Entity entt) const { + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr; + } + + [[nodiscard]] auto &sparse_ref(const Entity entt) const { + ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); + const auto pos = static_cast(traits_type::to_entity(entt)); + return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + [[nodiscard]] auto to_iterator(const Entity entt) const { + return --(end() - index(entt)); + } + + [[nodiscard]] auto &assure_at_least(const Entity entt) { + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + + if(!(page < sparse.size())) { + sparse.resize(page + 1u, nullptr); + } + + if(!sparse[page]) { + constexpr entity_type init = null; + auto page_allocator{packed.get_allocator()}; + sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size); + std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init); + } + + return sparse[page][fast_mod(pos, traits_type::page_size)]; + } + + void release_sparse_pages() { + auto page_allocator{packed.get_allocator()}; + + for(auto &&page: sparse) { + if(page != nullptr) { + std::destroy(page, page + traits_type::page_size); + alloc_traits::deallocate(page_allocator, page, traits_type::page_size); + page = nullptr; + } + } + } + + void swap_at(const std::size_t from, const std::size_t to) { + auto &lhs = packed[from]; + auto &rhs = packed[to]; + + sparse_ref(lhs) = traits_type::combine(static_cast(to), traits_type::to_integral(lhs)); + sparse_ref(rhs) = traits_type::combine(static_cast(from), traits_type::to_integral(rhs)); + + std::swap(lhs, rhs); + } + + underlying_type policy_to_head() { + return traits_type::entity_mask * (mode != deletion_policy::swap_only); + } + +private: + virtual const void *get_at(const std::size_t) const { + return nullptr; + } + + virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) { + ENTT_ASSERT((mode != deletion_policy::swap_only) || (((lhs < free_list()) + (rhs < free_list())) != 1u), "Cross swapping is not supported"); + } + +protected: + /*! @brief Random access iterator type. */ + using basic_iterator = internal::sparse_set_iterator; + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_only(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch"); + const auto pos = static_cast(index(*it)); + bump(traits_type::next(*it)); + swap_at(pos, static_cast(head -= (pos < head))); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_and_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch"); + auto &self = sparse_ref(*it); + const auto entt = traits_type::to_entity(self); + sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back())); + packed[static_cast(entt)] = packed.back(); + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((packed.back() = null, true), ""); + // lazy self-assignment guard + self = null; + packed.pop_back(); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void in_place_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch"); + const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null)); + packed[static_cast(entt)] = traits_type::combine(std::exchange(head, entt), tombstone); + } + +protected: + /** + * @brief Erases entities from a sparse set. + * @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. + */ + virtual void pop(basic_iterator first, basic_iterator last) { + switch(mode) { + case deletion_policy::swap_and_pop: + for(; first != last; ++first) { + swap_and_pop(first); + } + break; + case deletion_policy::in_place: + for(; first != last; ++first) { + in_place_pop(first); + } + break; + case deletion_policy::swap_only: + for(; first != last; ++first) { + swap_only(first); + } + break; + } + } + + /*! @brief Erases all entities of a sparse set. */ + virtual void pop_all() { + switch(mode) { + case deletion_policy::in_place: + if(head != traits_type::to_entity(null)) { + for(auto first = begin(); !(first.index() < 0); ++first) { + if(*first != tombstone) { + sparse_ref(*first) = null; + } + } + break; + } + [[fallthrough]]; + case deletion_policy::swap_only: + case deletion_policy::swap_and_pop: + for(auto first = begin(); !(first.index() < 0); ++first) { + sparse_ref(*first) = null; + } + break; + } + + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Assigns an entity to a sparse set. + * @param entt A valid identifier. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ + virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) { + auto &elem = assure_at_least(entt); + auto pos = size(); + + switch(mode) { + case deletion_policy::in_place: + if(head != traits_type::to_entity(null) && !force_back) { + pos = static_cast(head); + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(head, traits_type::to_integral(entt)); + head = traits_type::to_entity(std::exchange(packed[pos], entt)); + break; + } + [[fallthrough]]; + case deletion_policy::swap_and_pop: + packed.push_back(entt); + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + break; + case deletion_policy::swap_only: + if(elem == null) { + packed.push_back(entt); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + } else { + ENTT_ASSERT(!(traits_type::to_entity(elem) < head), "Slot not available"); + bump(entt); + } + + if(force_back) { + pos = static_cast(head++); + swap_at(static_cast(traits_type::to_entity(elem)), pos); + } + + break; + } + + return --(end() - pos); + } + +public: + /*! @brief Entity traits. */ + using traits_type = entt_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type to contained entities. */ + using pointer = typename packed_container_type::const_pointer; + /*! @brief Random access iterator type. */ + using iterator = basic_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + + /*! @brief Default constructor. */ + basic_sparse_set() + : basic_sparse_set{type_id()} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sparse_set(const allocator_type &allocator) + : basic_sparse_set{type_id(), deletion_policy::swap_and_pop, allocator} {} + + /** + * @brief Constructs an empty container with the given policy and allocator. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ + explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {}) + : basic_sparse_set{type_id(), pol, allocator} {} + + /** + * @brief Constructs an empty container with the given value type, policy + * and allocator. + * @param elem Returned value type, if any. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ + explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) + : sparse{allocator}, + packed{allocator}, + info{&elem}, + mode{pol}, + head{policy_to_head()} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sparse_set(basic_sparse_set &&other) noexcept + : sparse{std::move(other.sparse)}, + packed{std::move(other.packed)}, + info{other.info}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept + : sparse{std::move(other.sparse), allocator}, + packed{std::move(other.packed), allocator}, + info{other.info}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); + } + + /*! @brief Default destructor. */ + virtual ~basic_sparse_set() { + release_sparse_pages(); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This sparse set. + */ + basic_sparse_set &operator=(basic_sparse_set &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); + + release_sparse_pages(); + sparse = std::move(other.sparse); + packed = std::move(other.packed); + info = other.info; + mode = other.mode; + head = std::exchange(other.head, policy_to_head()); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given sparse set. + * @param other Sparse set to exchange the content with. + */ + void swap(basic_sparse_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(info, other.info); + swap(mode, other.mode); + swap(head, other.head); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return packed.get_allocator(); + } + + /** + * @brief Returns the deletion policy of a sparse set. + * @return The deletion policy of the sparse set. + */ + [[nodiscard]] deletion_policy policy() const noexcept { + return mode; + } + + /** + * @brief Returns the head of the free list, if any. + * @return The head of the free list. + */ + [[nodiscard]] size_type free_list() const noexcept { + return static_cast(head); + } + + /** + * @brief Sets the head of the free list, if possible. + * @param len The value to use as the new head of the free list. + */ + void free_list(const size_type len) noexcept { + ENTT_ASSERT((mode == deletion_policy::swap_only) && !(len > packed.size()), "Invalid value"); + head = static_cast(len); + } + + /** + * @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. + */ + virtual 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. + */ + [[nodiscard]] virtual size_type capacity() const noexcept { + return packed.capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + virtual void 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. + */ + [[nodiscard]] size_type extent() const noexcept { + return sparse.size() * traits_type::page_size; + } + + /** + * @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. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.size(); + } + + /** + * @brief Checks whether a sparse set is empty. + * @return True if the sparse set is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.empty(); + } + + /** + * @brief Checks whether a sparse set is fully packed. + * @return True if the sparse set is fully packed, false otherwise. + */ + [[nodiscard]] bool contiguous() const noexcept { + return (mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)); + } + + /** + * @brief Direct access to the internal packed array. + * @return A pointer to the internal packed array. + */ + [[nodiscard]] pointer data() const noexcept { + return packed.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the sparse set is empty, the returned iterator will be equal to + * `end()`. + * + * @return An iterator to the first entity of the sparse set. + */ + [[nodiscard]] iterator begin() const noexcept { + const auto pos = static_cast(packed.size()); + return iterator{packed, pos}; + } + + /*! @copydoc begin */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last entity of a sparse + * set. + */ + [[nodiscard]] iterator end() const noexcept { + return iterator{packed, {}}; + } + + /*! @copydoc end */ + [[nodiscard]] const_iterator cend() const noexcept { + return end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the sparse set is empty, the returned iterator will be equal to + * `rend()`. + * + * @return An iterator to the first entity of the reversed internal packed + * array. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return std::make_reverse_iterator(end()); + } + + /*! @copydoc rbegin */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last entity of the + * reversed sparse set. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return std::make_reverse_iterator(begin()); + } + + /*! @copydoc rend */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return rend(); + } + + /*! @copydoc begin Useful only in case of swap-only policy. */ + [[nodiscard]] iterator begin(int) const noexcept { + return (mode == deletion_policy::swap_only) ? (end() - static_cast(head)) : begin(); + } + + /*! @copydoc cbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_iterator cbegin(int) const noexcept { + return begin(0); + } + + /*! @copydoc end Useful only in case of swap-only policy. */ + [[nodiscard]] iterator end(int) const noexcept { + return end(); + } + + /*! @copydoc cend Useful only in case of swap-only policy. */ + [[nodiscard]] const_iterator cend(int) const noexcept { + return end(0); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] reverse_iterator rbegin(int) const noexcept { + return std::make_reverse_iterator(end(0)); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_reverse_iterator crbegin(int) const noexcept { + return rbegin(0); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] reverse_iterator rend(int) const noexcept { + return std::make_reverse_iterator(begin(0)); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_reverse_iterator crend(int) const noexcept { + return rend(0); + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] const_iterator find(const entity_type entt) const noexcept { + return contains(entt) ? to_iterator(entt) : end(); + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + const auto elem = sparse_ptr(entt); + constexpr auto cap = traits_type::entity_mask; + constexpr auto mask = traits_type::to_integral(null) & ~cap; + // testing versions permits to avoid accessing the packed array + return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap); + } + + /** + * @brief Returns the contained version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if present, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entt) const noexcept { + const auto elem = sparse_ptr(entt); + constexpr auto fallback = traits_type::to_version(tombstone); + return elem ? traits_type::to_version(*elem) : fallback; + } + + /** + * @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. + * + * @param entt A valid identifier. + * @return The position of the entity in the sparse set. + */ + [[nodiscard]] size_type index(const entity_type entt) const noexcept { + ENTT_ASSERT(contains(entt), "Set does not contain entity"); + return static_cast(traits_type::to_entity(sparse_ref(entt))); + } + + /** + * @brief Returns the entity at specified location, with bounds checking. + * @param pos The position for which to return the entity. + * @return The entity at specified location if any, a null entity otherwise. + */ + [[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type at(const size_type pos) const noexcept { + return pos < packed.size() ? packed[pos] : null; + } + + /** + * @brief Returns the entity at specified location, without bounds checking. + * @param pos The position for which to return the entity. + * @return The entity at specified location. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const noexcept { + ENTT_ASSERT(pos < packed.size(), "Position is out of bounds"); + return packed[pos]; + } + + /** + * @brief Returns the element assigned to an entity, if any. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior. + * + * @param entt A valid identifier. + * @return An opaque pointer to the element assigned to the entity, if any. + */ + [[nodiscard]] const void *value(const entity_type entt) const noexcept { + return get_at(index(entt)); + } + + /*! @copydoc value */ + [[nodiscard]] void *value(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).value(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. + * + * @param entt A valid identifier. + * @param elem Optional opaque element to forward to mixins, if any. + * @return Iterator pointing to the emplaced element in case of success, the + * `end()` iterator otherwise. + */ + iterator push(const entity_type entt, const void *elem = nullptr) { + return try_emplace(entt, (mode == deletion_policy::swap_only), elem); + } + + /** + * @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. + * + * @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. + * @return Iterator pointing to the first element inserted in case of + * success, the `end()` iterator otherwise. + */ + template + iterator push(It first, It last) { + for(auto it = first; it != last; ++it) { + try_emplace(*it, true); + } + + return first == last ? end() : find(*first); + } + + /** + * @brief Bump the version number of an entity. + * + * @warning + * Attempting to bump the version of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The version of the given identifier. + */ + version_type bump(const entity_type entt) { + auto &entity = sparse_ref(entt); + ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version"); + entity = traits_type::combine(traits_type::to_integral(entity), traits_type::to_integral(entt)); + packed[static_cast(traits_type::to_entity(entity))] = entt; + return traits_type::to_version(entt); + } + + /** + * @brief Erases an entity from a sparse set. + * + * @warning + * Attempting to erase an entity that doesn't belong to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + */ + void erase(const entity_type entt) { + const auto it = to_iterator(entt); + pop(it, it + 1u); + } + + /** + * @brief Erases entities from a set. + * + * @sa erase + * + * @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 erase(It first, It last) { + if constexpr(std::is_same_v) { + pop(first, last); + } else { + for(; first != last; ++first) { + erase(*first); + } + } + } + + /** + * @brief Removes an entity from a sparse set if it exists. + * @param entt A valid identifier. + * @return True if the entity is actually removed, false otherwise. + */ + bool remove(const entity_type entt) { + return contains(entt) && (erase(entt), true); + } + + /** + * @brief Removes entities from a sparse set if they exist. + * @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. + * @return The number of entities actually removed. + */ + template + size_type remove(It first, It last) { + size_type count{}; + + if constexpr(std::is_same_v) { + while(first != last) { + while(first != last && !contains(*first)) { + ++first; + } + + const auto it = first; + + while(first != last && contains(*first)) { + ++first; + } + + count += std::distance(it, first); + erase(it, first); + } + } else { + for(; first != last; ++first) { + count += remove(*first); + } + } + + return count; + } + + /*! @brief Removes all tombstones from a sparse set. */ + void compact() { + if(mode == deletion_policy::in_place) { + size_type from = packed.size(); + for(; from && packed[from - 1u] == tombstone; --from) {} + underlying_type pos = std::exchange(head, traits_type::entity_mask); + + while(pos != traits_type::to_entity(null)) { + if(const auto to = static_cast(std::exchange(pos, traits_type::to_entity(packed[pos]))); to < from) { + --from; + swap_or_move(from, to); + + packed[to] = packed[from]; + const auto entity = static_cast(to); + sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to])); + + for(; from && packed[from - 1u] == tombstone; --from) {} + } + } + + packed.erase(packed.begin() + from, packed.end()); + } + } + + /** + * @brief Swaps two entities in a sparse set. + * + * 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. + * + * @param lhs A valid identifier. + * @param rhs A valid identifier. + */ + void swap_elements(const entity_type lhs, const entity_type rhs) { + const auto from = index(lhs); + const auto to = index(rhs); + + // basic no-leak guarantee if swapping throws + swap_or_move(from, to); + swap_at(from, to); + } + + /** + * @brief Sort the first count elements according to the given comparison + * function. + * + * 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 object 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. + * + * @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 length Number of elements 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_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed"); + ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements"); + + algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward(args)...); + + for(size_type pos{}; pos < length; ++pos) { + auto curr = pos; + auto next = index(packed[curr]); + + while(curr != next) { + const auto idx = index(packed[next]); + const auto entt = packed[curr]; + + swap_or_move(next, idx); + const auto entity = static_cast(curr); + sparse_ref(entt) = traits_type::combine(entity, traits_type::to_integral(packed[curr])); + curr = std::exchange(next, idx); + } + } + } + + /** + * @brief Sort all elements according to the given comparison function. + * + * @sa sort_n + * + * @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) { + sort_n(static_cast(end(0) - begin(0)), std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort entities according to their order in a range. + * + * Entities that are part of both the sparse set and the range are ordered + * internally according to the order they have in the range.
+ * All other entities goes to the end of the sparse set and there are no + * guarantees on their order. + * + * @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 sort_as(It first, It last) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed"); + + for(auto it = begin(0); it.index() && first != last; ++first) { + if(const auto curr = *first; contains(curr)) { + if(const auto entt = *it; entt != curr) { + // basic no-leak guarantee (with invalid state) if swapping throws + swap_elements(entt, curr); + } + + ++it; + } + } + } + + /** + * @copybrief sort_as + * @param other The sparse sets that imposes the order of the entities. + */ + [[deprecated("use iterator based sort_as instead")]] void sort_as(const basic_sparse_set &other) { + sort_as(other.begin(), other.end()); + } + + /*! @brief Clears a sparse set. */ + void clear() { + pop_all(); + // sanity check to avoid subtle issues due to storage classes + ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set"); + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Returned value type, if any. + * @return Returned value type, if any. + */ + const type_info &type() const noexcept { + return *info; + } + + /*! @brief Forwards variables to derived classes, if any. */ + virtual void bind(any) noexcept {} + +private: + sparse_container_type sparse; + packed_container_type packed; + const type_info *info; + deletion_policy mode; + underlying_type head; +}; + +} // namespace entt + +#endif + +// #include "entity/storage.hpp" +#ifndef ENTT_ENTITY_STORAGE_HPP +#define ENTT_ENTITY_STORAGE_HPP + +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class storage_iterator final { + friend storage_iterator; + + using container_type = std::remove_const_t; + using alloc_traits = std::allocator_traits; + + using iterator_traits = std::iterator_traits, + typename alloc_traits::template rebind_traits::element_type>::const_pointer, + typename alloc_traits::template rebind_traits::element_type>::pointer>>; + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr storage_iterator() noexcept = default; + + constexpr storage_iterator(Container *ref, const difference_type idx) noexcept + : payload{ref}, + offset{idx} {} + + template, typename = std::enable_if_t> + constexpr storage_iterator(const storage_iterator, Size> &other) noexcept + : storage_iterator{other.payload, other.offset} {} + + constexpr storage_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr storage_iterator operator++(int) noexcept { + storage_iterator orig = *this; + return ++(*this), orig; + } + + constexpr storage_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr storage_iterator operator--(int) noexcept { + storage_iterator orig = *this; + return operator--(), orig; + } + + constexpr storage_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr storage_iterator operator+(const difference_type value) const noexcept { + storage_iterator copy = *this; + return (copy += value); + } + + constexpr storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + const auto pos = index() - value; + return (*payload)[pos / Size][fast_mod(pos, Size)]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + const auto pos = index(); + return (*payload)[pos / Size] + fast_mod(pos, Size); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + Container *payload; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class extended_storage_iterator final { + template + friend class extended_storage_iterator; + +public: + using iterator_type = It; + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::forward_as_tuple(*std::declval()...))); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_storage_iterator() + : it{} {} + + constexpr extended_storage_iterator(iterator_type base, Other... other) + : it{base, other...} {} + + template && ...) && (std::is_constructible_v && ...)>> + constexpr extended_storage_iterator(const extended_storage_iterator &other) + : it{other.it} {} + + constexpr extended_storage_iterator &operator++() noexcept { + return ++std::get(it), (++std::get(it), ...), *this; + } + + constexpr extended_storage_iterator operator++(int) noexcept { + extended_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {*std::get(it), *std::get(it)...}; + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return std::get(it); + } + + template + friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; + +private: + std::tuple it; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic storage implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @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. + * + * @tparam Type Type of objects assigned to the entities. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage: public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + + [[nodiscard]] auto &element_at(const std::size_t pos) const { + return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + auto assure_at_least(const std::size_t pos) { + const auto idx = pos / traits_type::page_size; + + if(!(idx < payload.size())) { + auto curr = payload.size(); + allocator_type allocator{get_allocator()}; + payload.resize(idx + 1u, nullptr); + + ENTT_TRY { + for(const auto last = payload.size(); curr < last; ++curr) { + payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size); + } + } + ENTT_CATCH { + payload.resize(curr); + ENTT_THROW; + } + } + + return payload[idx] + fast_mod(pos, traits_type::page_size); + } + + template + auto emplace_element(const Entity entt, const bool force_back, Args &&...args) { + const auto it = base_type::try_emplace(entt, force_back); + + ENTT_TRY { + auto elem = assure_at_least(static_cast(it.index())); + entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward(args)...); + } + ENTT_CATCH { + base_type::pop(it, it + 1u); + ENTT_THROW; + } + + return it; + } + + void shrink_to_size(const std::size_t sz) { + const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size; + allocator_type allocator{get_allocator()}; + + for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { + if constexpr(traits_type::in_place_delete) { + if(base_type::data()[pos] != tombstone) { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } else { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } + + for(auto pos = from, last = payload.size(); pos < last; ++pos) { + alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size); + } + + payload.resize(from); + } + +private: + const void *get_at(const std::size_t pos) const final { + return std::addressof(element_at(pos)); + } + + void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override { + static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v && std::is_move_assignable_v); + // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy + ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type"); + + if constexpr(!is_pinned_type_v) { + auto &elem = element_at(from); + + if constexpr(traits_type::in_place_delete) { + if(base_type::operator[](to) == tombstone) { + allocator_type allocator{get_allocator()}; + entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), allocator, std::move(elem)); + alloc_traits::destroy(allocator, std::addressof(elem)); + return; + } + } + + using std::swap; + swap(elem, element_at(to)); + } + } + +protected: + /** + * @brief Erases entities from a storage. + * @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. + */ + void pop(underlying_iterator first, underlying_iterator last) override { + for(allocator_type allocator{get_allocator()}; first != last; ++first) { + // cannot use first.index() because it would break with cross iterators + auto &elem = element_at(base_type::index(*first)); + + if constexpr(traits_type::in_place_delete) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(elem)); + } else { + auto &other = element_at(base_type::size() - 1u); + // destroying on exit allows reentrant destructors + [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); + alloc_traits::destroy(allocator, std::addressof(other)); + base_type::swap_and_pop(first); + } + } + } + + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + allocator_type allocator{get_allocator()}; + + for(auto first = base_type::begin(); !(first.index() < 0); ++first) { + if constexpr(traits_type::in_place_delete) { + if(*first != tombstone) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } else { + base_type::swap_and_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } + } + + /** + * @brief Assigns an entity to a storage. + * @param entt A valid identifier. + * @param value Optional opaque value. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { + if(value) { + if constexpr(std::is_copy_constructible_v) { + return emplace_element(entt, force_back, *static_cast(value)); + } else { + return base_type::end(); + } + } else { + if constexpr(std::is_default_constructible_v) { + return emplace_element(entt, force_back); + } else { + return base_type::end(); + } + } + } + +public: + /*! @brief Base type. */ + using base_type = underlying_type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Component traits. */ + using traits_type = component_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type to contained elements. */ + using pointer = typename container_type::pointer; + /*! @brief Constant pointer type to contained elements. */ + using const_pointer = typename alloc_traits::template rebind_traits::const_pointer; + /*! @brief Random access iterator type. */ + using iterator = internal::storage_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::storage_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), deletion_policy{traits_type::in_place_delete}, allocator}, + payload{allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)}, + payload{std::move(other.payload)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator}, + payload{std::move(other.payload), allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed"); + } + + /*! @brief Default destructor. */ + ~basic_storage() override { + shrink_to_size(0u); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed"); + + shrink_to_size(0u); + base_type::operator=(std::move(other)); + payload = std::move(other.payload); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_storage &other) { + using std::swap; + base_type::swap(other); + swap(payload, other.payload); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return payload.get_allocator(); + } + + /** + * @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) override { + if(cap != 0u) { + base_type::reserve(cap); + assure_at_least(cap - 1u); + } + } + + /** + * @brief Returns the number of elements that a storage has currently + * allocated space for. + * @return Capacity of the storage. + */ + [[nodiscard]] size_type capacity() const noexcept override { + return payload.size() * traits_type::page_size; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() override { + base_type::shrink_to_fit(); + shrink_to_size(base_type::size()); + } + + /** + * @brief Direct access to the array of objects. + * @return A pointer to the array of objects. + */ + [[nodiscard]] const_pointer raw() const noexcept { + return payload.data(); + } + + /*! @copydoc raw */ + [[nodiscard]] pointer raw() noexcept { + return payload.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + const auto pos = static_cast(base_type::size()); + return const_iterator{&payload, pos}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + const auto pos = static_cast(base_type::size()); + return iterator{&payload, pos}; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return const_iterator{&payload, {}}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return iterator{&payload, {}}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ + [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { + return element_at(base_type::index(entt)); + } + + /*! @copydoc get */ + [[nodiscard]] value_type &get(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).get(entt)); + } + + /** + * @brief Returns the object assigned to an entity as a tuple. + * @param entt A valid identifier. + * @return The object assigned to the entity as a tuple. + */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { + return std::forward_as_tuple(get(entt)); + } + + /*! @copydoc get_as_tuple */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { + return std::forward_as_tuple(get(entt)); + } + + /** + * @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. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. + */ + template + value_type &emplace(const entity_type entt, Args &&...args) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + const auto it = emplace_element(entt, false, Type{std::forward(args)...}); + return element_at(static_cast(it.index())); + } else { + const auto it = emplace_element(entt, false, std::forward(args)...); + return element_at(static_cast(it.index())); + } + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the updated instance. + */ + template + value_type &patch(const entity_type entt, Func &&...func) { + const auto idx = base_type::index(entt); + auto &elem = element_at(idx); + (std::forward(func)(elem), ...); + return elem; + } + + /** + * @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. + * + * @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. + * @return Iterator pointing to the last element inserted, if any. + */ + template + iterator insert(It first, It last, const value_type &value = {}) { + for(; first != last; ++first) { + emplace_element(*first, true, value); + } + + return begin(); + } + + /** + * @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. + * @return Iterator pointing to the first element inserted, if any. + */ + template::value_type, value_type>>> + iterator insert(EIt first, EIt last, CIt from) { + for(; first != last; ++first, ++from) { + emplace_element(*first, true, *from); + } + + return begin(); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its component. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin(), rbegin()}, internal::extended_storage_iterator{base_type::rend(), rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin(), crbegin()}, internal::extended_storage_iterator{base_type::crend(), crend()}}; + } + +private: + container_type payload; +}; + +/*! @copydoc basic_storage */ +template +class basic_storage::page_size == 0u>> + : public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + +public: + /*! @brief Base type. */ + using base_type = basic_sparse_set>; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Component traits. */ + using traits_type = component_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), deletion_policy{traits_type::in_place_delete}, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + // std::allocator has no cross constructors (waiting for C++20) + if constexpr(std::is_void_v && !std::is_constructible_v) { + return allocator_type{}; + } else { + return allocator_type{base_type::get_allocator()}; + } + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + } + + /** + * @brief Returns an empty tuple. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + return std::tuple{}; + } + + /** + * @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. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + */ + template + void emplace(const entity_type entt, Args &&...) { + base_type::try_emplace(entt, false); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of optional arguments. + * @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, Args &&...) { + for(; first != last; ++first) { + base_type::try_emplace(*first, true); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}}; + } +}; + +/** + * @brief Swap-only entity storage specialization. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage + : public basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + + auto entity_at(const std::size_t pos) const noexcept { + ENTT_ASSERT(pos < underlying_type::traits_type::to_entity(null), "Invalid element"); + return underlying_type::traits_type::combine(static_cast(pos), {}); + } + +protected: + /** + * @brief Assigns an entity to a storage. + * @param hint A valid identifier. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace(const Entity hint, const bool, const void *) override { + return base_type::find(emplace(hint)); + } + +public: + /*! @brief Base type. */ + using base_type = basic_sparse_set; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Entity; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} { + } + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), deletion_policy::swap_only, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + base_type::operator=(std::move(other)); + return *this; + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + } + + /** + * @brief Returns an empty tuple. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + return std::tuple{}; + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @return A valid identifier. + */ + entity_type emplace() { + const auto len = base_type::free_list(); + const auto entt = (len == base_type::size()) ? entity_at(len) : base_type::data()[len]; + return *base_type::try_emplace(entt, true); + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * + * If the requested identifier isn't in use, the suggested one is used. + * Otherwise, a new identifier is returned. + * + * @param hint Required identifier. + * @return A valid identifier. + */ + entity_type emplace(const entity_type hint) { + if(hint == null || hint == tombstone) { + return emplace(); + } else if(const auto curr = underlying_type::traits_type::construct(underlying_type::traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) { + const auto pos = static_cast(underlying_type::traits_type::to_entity(hint)); + const auto entt = *base_type::try_emplace(hint, true); + + while(!(pos < base_type::size())) { + base_type::try_emplace(entity_at(base_type::size() - 1u), false); + } + + return entt; + } else if(const auto idx = base_type::index(curr); idx < base_type::free_list()) { + return emplace(); + } else { + return *base_type::try_emplace(hint, true); + } + } + + /** + * @brief Updates a given identifier. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns each element in a range an identifier. + * @tparam It Type of mutable 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 insert(It first, It last) { + for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) { + *first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true); + } + + for(; first != last; ++first) { + *first = *base_type::try_emplace(entity_at(base_type::free_list()), true); + } + } + + /** + * @brief Makes all elements in a range contiguous. + * @tparam It Type of forward iterator. + * @param first An iterator to the first element of the range to pack. + * @param last An iterator past the last element of the range to pack. + * @return The number of elements within the newly created range. + */ + template + [[deprecated("use sort_as instead")]] size_type pack(It first, It last) { + base_type::sort_as(first, last); + return static_cast(std::distance(first, last)); + } + + /** + * @brief Returns the number of elements considered still in use. + * @return The number of elements considered still in use. + */ + [[deprecated("use free_list() instead")]] [[nodiscard]] size_type in_use() const noexcept { + return base_type::free_list(); + } + + /** + * @brief Sets the number of elements considered still in use. + * @param len The number of elements considered still in use. + */ + [[deprecated("use free_list(len) instead")]] void in_use(const size_type len) noexcept { + base_type::free_list(len); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin(0)}, internal::extended_storage_iterator{base_type::end(0)}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin(0)}, internal::extended_storage_iterator{base_type::cend(0)}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend(0)}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend(0)}}; + } +}; + +} // namespace entt + +#endif + +// #include "entity/view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +[[nodiscard]] bool all_of_but(const std::size_t index, const Type *const *it, const std::size_t len, const Entity entt) noexcept { + std::size_t pos{}; + for(; (pos != index) && it[pos]->contains(entt); ++pos) {} + + if(pos == index) { + for(++pos; (pos != len) && it[pos]->contains(entt); ++pos) {} + } + + return pos == len; +} + +template +[[nodiscard]] bool none_of(const Type *const *it, const std::size_t len, const Entity entt) noexcept { + std::size_t pos{}; + for(; (pos != len) && !(it[pos] && it[pos]->contains(entt)); ++pos) {} + return pos == len; +} + +template +[[nodiscard]] bool fully_initialized(const Type *const *it, const std::size_t len) noexcept { + std::size_t pos{}; + for(; (pos != len) && it[pos]; ++pos) {} + return pos == len; +} + +template +[[nodiscard]] Result view_pack(const View &view, const Other &other, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence) { + Result elem{}; + // friend-initialization, avoid multiple calls to refresh + elem.pools = {view.template storage()..., other.template storage()...}; + elem.filter = {view.template storage()..., other.template storage()...}; + elem.refresh(); + return elem; +} + +template +class view_iterator final { + using iterator_type = typename Type::const_iterator; + + [[nodiscard]] bool valid(const typename iterator_type::value_type entt) const noexcept { + return ((Get != 1u) || (entt != tombstone)) && all_of_but(index, pools.data(), Get, entt) && none_of(filter.data(), Exclude, entt); + } + +public: + using value_type = typename iterator_type::value_type; + using pointer = typename iterator_type::pointer; + using reference = typename iterator_type::reference; + using difference_type = typename iterator_type::difference_type; + using iterator_category = std::forward_iterator_tag; + + constexpr view_iterator() noexcept + : it{}, + last{}, + pools{}, + filter{}, + index{} {} + + view_iterator(iterator_type curr, iterator_type to, std::array value, std::array excl, const std::size_t idx) noexcept + : it{curr}, + last{to}, + pools{value}, + filter{excl}, + index{idx} { + while(it != last && !valid(*it)) { + ++it; + } + } + + view_iterator &operator++() noexcept { + while(++it != last && !valid(*it)) {} + return *this; + } + + view_iterator operator++(int) noexcept { + view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return &*it; + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; + +private: + iterator_type it; + iterator_type last; + std::array pools; + std::array filter; + std::size_t index; +}; + +template +[[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +struct extended_view_iterator final { + using iterator_type = It; + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_view_iterator() + : it{}, + pools{} {} + + extended_view_iterator(iterator_type from, std::tuple value) + : it{from}, + pools{value} {} + + extended_view_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_view_iterator operator++(int) noexcept { + extended_view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief View implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + * + * @b Important + * + * View iterators aren't invalidated if: + * + * * New elements are added to the storage iterated by the view. + * * The entity currently returned is modified (for example, components are + * added or removed from it). + * * The entity currently returned is destroyed. + * + * In all other cases, modifying the storage iterated by a view in any way can + * invalidate all iterators. + */ +template +class basic_view; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + * @tparam Get Number of storage iterated by the view. + * @tparam Exclude Number of storage used to filter the view. + */ +template +class basic_common_view { + template + friend Return internal::view_pack(const View &, const Other &, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence); + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_common_view() noexcept = default; + + basic_common_view(std::array value, std::array excl) noexcept + : pools{value}, + filter{excl}, + leading{}, + index{Get} { + unchecked_refresh(); + } + + void use(const std::size_t pos) noexcept { + if(leading) { + index = pos; + leading = pools[index]; + } + } + + void unchecked_refresh() noexcept { + index = 0u; + + for(size_type pos{1u}; pos < Get; ++pos) { + if(pools[pos]->size() < pools[index]->size()) { + index = pos; + } + } + + leading = pools[index]; + } + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::view_iterator; + + /*! @brief Updates the internal leading view if required. */ + void refresh() noexcept { + size_type pos = (leading != nullptr) * Get; + for(; pos < Get && pools[pos] != nullptr; ++pos) {} + + if(pos == Get) { + unchecked_refresh(); + } + } + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return leading; + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const noexcept { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const noexcept { + return leading ? iterator{leading->begin(0), leading->end(0), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return leading ? iterator{leading->end(0), leading->end(0), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + if(leading) { + auto it = leading->rbegin(0); + const auto last = leading->rend(0); + for(; it != last && !contains(*it); ++it) {} + return it == last ? null : *it; + } + + return null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return contains(entt) ? iterator{leading->find(entt), leading->end(), pools, filter, index} : end(); + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return leading && internal::fully_initialized(filter.data(), Exclude); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + if(leading) { + const auto idx = leading->find(entt).index(); + return (!(idx < 0 || idx > leading->begin(0).index())) && internal::all_of_but(index, pools.data(), Get, entt) && internal::none_of(filter.data(), Exclude, entt); + } + + return false; + } + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + std::array pools{}; + std::array filter{}; + const common_type *leading{}; + size_type index{Get}; + /*! @endcond */ +}; + +/** + * @brief General purpose view. + * + * This view visits all entities that are at least in the given storage. During + * initialization, it also looks at the number of elements available for each + * storage and uses the smallest set in order to get a performance boost. + * + * @sa basic_view + * + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template +class basic_view, exclude_t>: public basic_common_view, sizeof...(Get), sizeof...(Exclude)> { + using base_type = basic_common_view, sizeof...(Get), sizeof...(Exclude)>; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + template + auto storage(std::index_sequence) const noexcept { + return std::make_tuple(storage()...); + } + + template + [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { + if constexpr(Curr == Other) { + return std::forward_as_tuple(std::get(curr)...); + } else { + return storage()->get_as_tuple(std::get<0>(curr)); + } + } + + template + void each(Func &func, std::index_sequence) const { + for(const auto curr: storage()->each()) { + if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && internal::all_of_but(this->index, this->pools.data(), sizeof...(Get), entt) && internal::none_of(this->filter.data(), sizeof...(Exclude), entt)) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); + } else { + std::apply(func, std::tuple_cat(dispatch_get(curr)...)); + } + } + } + } + + template + void pick_and_each(Func &func, std::index_sequence seq) const { + ((storage() == base_type::handle() ? each(func, seq) : void()), ...); + } + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Bidirectional iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Iterable view type. */ + using iterable = iterable_adaptor>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(Get &...value, Exclude &...excl) noexcept + : base_type{{&value...}, {&excl...}} { + } + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(std::tuple value, std::tuple excl = {}) noexcept + : basic_view{std::make_from_tuple(std::tuple_cat(value, excl))} {} + + /** + * @brief Forces a view to use a given component to drive iterations + * @tparam Type Type of component to use to drive iterations. + */ + template + void use() noexcept { + use>(); + } + + /** + * @brief Forces a view to use a given component to drive iterations + * @tparam Index Index of the component to use to drive iterations. + */ + template + void use() noexcept { + base_type::use(Index); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t>; + + if constexpr(Index < sizeof...(Get)) { + return static_cast(const_cast *>(this->pools[Index])); + } else { + return static_cast(const_cast *>(this->filter[Index - sizeof...(Get)])); + } + } + + /** + * @brief Assigns a storage to a view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + storage>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + static_assert(std::is_convertible_v> &>, "Unexpected type"); + + if constexpr(Index < sizeof...(Get)) { + this->pools[Index] = &elem; + base_type::refresh(); + } else { + this->filter[Index - sizeof...(Get)] = &elem; + } + } + + /** + * @brief Returns the components assigned to the given entity. + * @param entt A valid identifier. + * @return The components assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return get(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, storage(std::index_sequence_for{})); + } else if constexpr(sizeof...(Index) == 1) { + return (storage()->get(entt), ...); + } else { + return std::tuple_cat(storage()->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if(base_type::handle() != nullptr) { + pick_and_each(func, std::index_sequence_for{}); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + const auto as_pools = storage(std::index_sequence_for{}); + return {internal::extended_view_iterator{base_type::begin(), as_pools}, internal::extended_view_iterator{base_type::end(), as_pools}}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Component list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + */ +template +class basic_storage_view { +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_storage_view() noexcept = default; + + basic_storage_view(const Type *value) noexcept + : leading{value} {} + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename common_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return leading; + } + + /** + * @brief Returns the number of entities that have the given component. + * @return Number of entities that have the given component. + */ + [[nodiscard]] size_type size() const noexcept { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !leading || leading->empty(); + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const noexcept { + return leading ? leading->begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return leading ? leading->end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed view. + * + * If the view is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed view. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return leading ? leading->rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return leading ? leading->rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + return empty() ? null : *leading->begin(); + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + return empty() ? null : *leading->rbegin(); + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return leading ? leading->find(entt) : iterator{}; + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (leading != nullptr); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return leading && leading->contains(entt); + } + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + const common_type *leading{}; + /*! @endcond */ +}; + +/** + * @brief Storage view specialization. + * + * This specialization offers a boost in terms of performance. It can access the + * underlying data structure directly and avoid superfluous checks. + * + * @sa basic_view + * + * @tparam Get Type of storage iterated by the view. + */ +template +class basic_view, exclude_t<>, std::void_t>>: public basic_storage_view { + using base_type = basic_storage_view; + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Random access iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename base_type::reverse_iterator; + /*! @brief Iterable view type. */ + using iterable = decltype(std::declval().each()); + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(Get &value) noexcept + : base_type{&value} { + } + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(std::tuple value, std::tuple<> = {}) noexcept + : basic_view{std::get<0>(value)} {} + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return storage<0>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(Index == 0u, "Index out of bounds"); + return static_cast(const_cast *>(this->leading)); + } + + /** + * @brief Assigns a storage to a view. + * @param elem A storage to assign to the view. + */ + void storage(Get &elem) noexcept { + storage<0>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Get &elem) noexcept { + static_assert(Index == 0u, "Index out of bounds"); + this->leading = &elem; + } + + /** + * @brief Returns the component assigned to the given entity. + * @param entt A valid identifier. + * @return The component assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return storage()->get(entt); + } + + /** + * @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. + */ + [[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type operator[](const size_type pos) const { + return base_type::begin()[pos]; + } + + /** + * @brief Returns the component assigned to the given entity. + * @tparam Elem Type of the component to get. + * @param entt A valid identifier. + * @return The component assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return get<0>(entt); + } + + /** + * @brief Returns the component assigned to the given entity. + * @tparam Index Index of the component to get. + * @param entt A valid identifier. + * @return The component assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return storage()->get_as_tuple(entt); + } else { + return storage()->get(entt); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &); + * void(typename Type &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if(auto *elem = storage(); elem) { + if constexpr(is_applicable_veach().begin())>) { + for(const auto pack: elem->each()) { + std::apply(func, pack); + } + } else if constexpr(std::is_invocable_vbegin())>) { + for(auto &&component: *elem) { + func(component); + } + } else { + for(size_type pos = elem->size(); pos; --pos) { + func(); + } + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its component if it's a non-empty one. The _constness_ of + * the component is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + auto *elem = storage(); + return elem ? elem->each() : iterable{}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Component list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for<>{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template +basic_view(Type &...storage) -> basic_view, exclude_t<>>; + +/** + * @brief Deduction guide. + * @tparam Get Types of components iterated by the view. + * @tparam Exclude Types of components used to filter the view. + */ +template +basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; + +} // namespace entt + +#endif + +// #include "graph/adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_GRAPH_FWD_HPP +#define ENTT_GRAPH_FWD_HPP + +#include +#include +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Undirected graph category tag. */ +struct directed_tag {}; + +/*! @brief Directed graph category tag. */ +struct undirected_tag: directed_tag {}; + +template> +class adjacency_matrix; + +template> +class basic_flow; + +/*! @brief Alias declaration for the most common use case. */ +using flow = basic_flow<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; + } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "graph/dot.hpp" +#ifndef ENTT_GRAPH_DOT_HPP +#define ENTT_GRAPH_DOT_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Outputs a graph in dot format. + * @tparam Graph Graph type, valid as long as it exposes edges and vertices. + * @tparam Writer Vertex decorator type. + * @param out A standard output stream. + * @param graph The graph to output. + * @param writer Vertex decorator object. + */ +template +void dot(std::ostream &out, const Graph &graph, Writer writer) { + static_assert(std::is_base_of_v, "Invalid graph category"); + + if constexpr(std::is_same_v) { + out << "graph{"; + } else { + out << "digraph{"; + } + + for(auto &&vertex: graph.vertices()) { + out << vertex << "["; + writer(out, vertex); + out << "];"; + } + + for(auto [lhs, rhs]: graph.edges()) { + if constexpr(std::is_same_v) { + out << lhs << "--" << rhs << ";"; + } else { + out << lhs << "->" << rhs << ";"; + } + } + + out << "}"; +} + +/** + * @brief Outputs a graph in dot format. + * @tparam Graph Graph type, valid as long as it exposes edges and vertices. + * @param out A standard output stream. + * @param graph The graph to output. + */ +template +void dot(std::ostream &out, const Graph &graph) { + return dot(out, graph, [](auto &&...) {}); +} + +} // namespace entt + +#endif + +// #include "graph/flow.hpp" +#ifndef ENTT_GRAPH_FLOW_HPP +#define ENTT_GRAPH_FLOW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it->second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[offset].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[offset].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; + } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class for creating task graphs. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_flow { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using task_container_type = dense_set, typename alloc_traits::template rebind_alloc>; + using ro_rw_container_type = std::vector, typename alloc_traits::template rebind_alloc>>; + using deps_container_type = dense_map, typename alloc_traits::template rebind_alloc>>; + using adjacency_matrix_type = adjacency_matrix>; + + void emplace(const id_type res, const bool is_rw) { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + + if(!deps.contains(res) && sync_on != vertices.size()) { + deps[res].emplace_back(sync_on, true); + } + + deps[res].emplace_back(index.first(), is_rw); + } + + void setup_graph(adjacency_matrix_type &matrix) const { + for(const auto &elem: deps) { + const auto last = elem.second.cend(); + auto it = elem.second.cbegin(); + + while(it != last) { + if(it->second) { + // rw item + if(auto curr = it++; it != last) { + if(it->second) { + matrix.insert(curr->first, it->first); + } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + matrix.insert(it->first, next->first); + } + } else { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + } + } + } + } else { + // ro item (first iteration only) + if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(it->first, next->first); + } + } else { + it = last; + } + } + } + } + } + + void transitive_closure(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vk{}; vk < length; ++vk) { + for(std::size_t vi{}; vi < length; ++vi) { + for(std::size_t vj{}; vj < length; ++vj) { + if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { + matrix.insert(vi, vj); + } + } + } + } + } + + void transitive_reduction(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vert{}; vert < length; ++vert) { + matrix.erase(vert, vert); + } + + for(std::size_t vj{}; vj < length; ++vj) { + for(std::size_t vi{}; vi < length; ++vi) { + if(matrix.contains(vi, vj)) { + for(std::size_t vk{}; vk < length; ++vk) { + if(matrix.contains(vj, vk)) { + matrix.erase(vi, vk); + } + } + } + } + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Iterable task list. */ + using iterable = iterable_adaptor; + /*! @brief Adjacency matrix type. */ + using graph_type = adjacency_matrix_type; + + /*! @brief Default constructor. */ + basic_flow() + : basic_flow{allocator_type{}} {} + + /** + * @brief Constructs a flow builder with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_flow(const allocator_type &allocator) + : index{0u, allocator}, + vertices{allocator}, + deps{allocator}, + sync_on{} {} + + /*! @brief Default copy constructor. */ + basic_flow(const basic_flow &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_flow(const basic_flow &other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{other.vertices, allocator}, + deps{other.deps, allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default move constructor. */ + basic_flow(basic_flow &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_flow(basic_flow &&other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{std::move(other.vertices), allocator}, + deps{std::move(other.deps), allocator}, + sync_on{other.sync_on} {} + + /** + * @brief Default copy assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(const basic_flow &) = default; + + /** + * @brief Default move assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(basic_flow &&) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return allocator_type{index.second()}; + } + + /** + * @brief Returns the identifier at specified location. + * @param pos Position of the identifier to return. + * @return The requested identifier. + */ + [[nodiscard]] id_type operator[](const size_type pos) const { + return vertices.cbegin()[pos]; + } + + /*! @brief Clears the flow builder. */ + void clear() noexcept { + index.first() = {}; + vertices.clear(); + deps.clear(); + sync_on = {}; + } + + /** + * @brief Exchanges the contents with those of a given flow builder. + * @param other Flow builder to exchange the content with. + */ + void swap(basic_flow &other) { + using std::swap; + std::swap(index, other.index); + std::swap(vertices, other.vertices); + std::swap(deps, other.deps); + std::swap(sync_on, other.sync_on); + } + + /** + * @brief Returns the number of tasks. + * @return The number of tasks. + */ + [[nodiscard]] size_type size() const noexcept { + return vertices.size(); + } + + /** + * @brief Binds a task to a flow builder. + * @param value Task identifier. + * @return This flow builder. + */ + basic_flow &bind(const id_type value) { + sync_on += (sync_on == vertices.size()); + const auto it = vertices.emplace(value).first; + index.first() = size_type(it - vertices.begin()); + return *this; + } + + /** + * @brief Turns the current task into a sync point. + * @return This flow builder. + */ + basic_flow &sync() { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + sync_on = index.first(); + + for(const auto &elem: deps) { + elem.second.emplace_back(sync_on, true); + } + + return *this; + } + + /** + * @brief Assigns a resource to the current task with a given access mode. + * @param res Resource identifier. + * @param is_rw Access mode. + * @return This flow builder. + */ + basic_flow &set(const id_type res, bool is_rw = false) { + emplace(res, is_rw); + return *this; + } + + /** + * @brief Assigns a read-only resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &ro(const id_type res) { + emplace(res, false); + return *this; + } + + /** + * @brief Assigns a range of read-only resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + ro(It first, It last) { + for(; first != last; ++first) { + emplace(*first, false); + } + + return *this; + } + + /** + * @brief Assigns a writable resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &rw(const id_type res) { + emplace(res, true); + return *this; + } + + /** + * @brief Assigns a range of writable resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + rw(It first, It last) { + for(; first != last; ++first) { + emplace(*first, true); + } + + return *this; + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency matrix of the task graph. + */ + [[nodiscard]] graph_type graph() const { + graph_type matrix{vertices.size(), get_allocator()}; + + setup_graph(matrix); + transitive_closure(matrix); + transitive_reduction(matrix); + + return matrix; + } + +private: + compressed_pair index; + task_container_type vertices; + deps_container_type deps; + size_type sync_on; +}; + +} // namespace entt + +#endif + +// #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 + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/** + * @brief Service locator, nothing more. + * + * A service locator is 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 tiny class + * tries to fill the gap and to get rid of the burden of defining a different + * specific locator for each application. + * + * @note + * Users shouldn't retain references to a service. The recommended way is to + * retrieve the service implementation currently set each and every time the + * need for it arises. The risk is to incur in unexpected behaviors otherwise. + * + * @tparam Service Service type. + */ +template +class locator final { + class service_handle { + friend class locator; + std::shared_ptr value{}; + }; + +public: + /*! @brief Service type. */ + using type = Service; + /*! @brief Service node type. */ + using node_type = service_handle; + + /*! @brief Default constructor, deleted on purpose. */ + locator() = delete; + /*! @brief Default destructor, deleted on purpose. */ + ~locator() = delete; + + /** + * @brief Checks whether a service locator contains a value. + * @return True if the service locator contains a value, false otherwise. + */ + [[nodiscard]] static bool has_value() noexcept { + return (service != nullptr); + } + + /** + * @brief Returns a reference to a valid service, if any. + * + * @warning + * Invoking this function can result in undefined behavior if the service + * hasn't been set yet. + * + * @return A reference to the service currently set, if any. + */ + [[nodiscard]] static Service &value() noexcept { + ENTT_ASSERT(has_value(), "Service not available"); + return *service; + } + + /** + * @brief Returns a service if available or sets it from a fallback type. + * + * Arguments are used only if a service doesn't already exist. In all other + * cases, they are discarded. + * + * @tparam Args Types of arguments to use to construct the fallback service. + * @tparam Type Fallback service type. + * @param args Parameters to use to construct the fallback service. + * @return A reference to a valid service. + */ + template + [[nodiscard]] static Service &value_or(Args &&...args) { + return service ? *service : emplace(std::forward(args)...); + } + + /** + * @brief Sets or replaces a service. + * @tparam Type Service type. + * @tparam Args Types of arguments to use to construct the service. + * @param args Parameters to use to construct the service. + * @return A reference to a valid service. + */ + template + static Service &emplace(Args &&...args) { + service = std::make_shared(std::forward(args)...); + return *service; + } + + /** + * @brief Sets or replaces a service using a given allocator. + * @tparam Type Service type. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the service. + * @param alloc The allocator to use. + * @param args Parameters to use to construct the service. + * @return A reference to a valid service. + */ + template + static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) { + service = std::allocate_shared(alloc, std::forward(args)...); + return *service; + } + + /** + * @brief Returns a handle to the underlying service. + * @return A handle to the underlying service. + */ + static node_type handle() noexcept { + node_type node{}; + node.value = service; + return node; + } + + /** + * @brief Resets or replaces a service. + * @param other Optional handle with which to replace the service. + */ + static void reset(const node_type &other = {}) noexcept { + service = other.value; + } + + /** + * @brief Resets or replaces a service. + * @tparam Type Service type. + * @tparam Deleter Deleter type. + * @param elem A pointer to a service to manage. + * @param deleter A deleter to use to destroy the service. + */ + template> + static void reset(Type *elem, Deleter deleter = {}) { + service = std::shared_ptr{elem, std::move(deleter)}; + } + +private: + // std::shared_ptr because of its type erased allocator which is useful here + inline static std::shared_ptr service{}; +}; + +} // namespace entt + +#endif + +// #include "meta/adl_pointer.hpp" +#ifndef ENTT_META_ADL_POINTER_HPP +#define ENTT_META_ADL_POINTER_HPP + +namespace entt { + +/** + * @brief ADL based lookup function for dereferencing meta pointer-like types. + * @tparam Type Element type. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +template +decltype(auto) dereference_meta_pointer_like(const Type &value) { + return *value; +} + +/** + * @brief Fake ADL based lookup function for meta pointer-like types. + * @tparam Type Element type. + */ +template +struct adl_meta_pointer_like { + /** + * @brief Uses the default ADL based lookup method to resolve the call. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ + static decltype(auto) dereference(const Type &value) { + return dereference_meta_pointer_like(value); + } +}; + +} // namespace entt + +#endif + +// #include "meta/container.hpp" +#ifndef ENTT_META_CONTAINER_HPP +#define ENTT_META_CONTAINER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it->second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[offset].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[offset].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +#endif + +// #include "context.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +// #include "../container/dense_map.hpp" + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + + +namespace entt { + +class meta_ctx; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct meta_type_node; + +struct meta_context { + dense_map value{}; + + [[nodiscard]] inline static meta_context &from(meta_ctx &ctx); + [[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx); +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Disambiguation tag for constructors and the like. */ +class meta_ctx_arg_t final {}; + +/*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */ +inline constexpr meta_ctx_arg_t meta_ctx_arg{}; + +/*! @brief Opaque meta context type. */ +class meta_ctx: private internal::meta_context { + // attorney idiom like model to access the base class + friend struct internal::meta_context; +}; + +/*! @cond TURN_OFF_DOXYGEN */ +[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { + return ctx; +} + +[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { + return ctx; +} +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "meta.hpp" +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #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 "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr; + size_type length; + hash_type hash; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers 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. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{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. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> 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 +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @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 differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @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 first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @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 first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @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 first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @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 first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) 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; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, the object owns the contained element. */ + owner, + /*! @brief Aliasing mode, the object _points_ to a non-const element. */ + ref, + /*! @brief Const aliasing mode, the object _points_ to a const element. */ + cref +}; + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + using operation = internal::any_operation; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; + + template + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { + static_assert(!std::is_void_v && std::is_same_v>, Type>, "Invalid type"); + const Type *element = nullptr; + + if constexpr(in_situ) { + element = (value.mode == any_policy::owner) ? reinterpret_cast(&value.storage) : static_cast(value.instance); + } else { + element = static_cast(value.instance); + } + + switch(op) { + case operation::copy: + if constexpr(std::is_copy_constructible_v) { + static_cast(const_cast(other))->initialize(*element); + } + break; + case operation::move: + if constexpr(in_situ) { + if(value.mode == any_policy::owner) { + return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; + } + } + + return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); + case operation::transfer: + if constexpr(std::is_move_assignable_v) { + *const_cast(element) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case operation::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(element) = *static_cast(other); + return other; + } + break; + case operation::destroy: + if constexpr(in_situ) { + element->~Type(); + } else if constexpr(std::is_array_v) { + delete[] element; + } else { + delete element; + } + break; + case operation::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return *element == *static_cast(other) ? other : nullptr; + } else { + return (element == other) ? other : nullptr; + } + case operation::get: + return element; + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + + if constexpr(!std::is_void_v) { + vtable = basic_vtable>>; + + if constexpr(std::is_lvalue_reference_v) { + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + instance = (std::addressof(args), ...); + } else if constexpr(in_situ>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; + } else { + new(&storage) std::remove_cv_t>(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + instance = new std::remove_cv_t>{std::forward(args)...}; + } else { + instance = new std::remove_cv_t>(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const any_policy pol) noexcept + : instance{other.data()}, + info{other.info}, + vtable{other.vtable}, + mode{pol} {} + +public: + /*! @brief Size of the internal storage. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : instance{}, + info{}, + vtable{}, + mode{any_policy::owner} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{} { + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : instance{}, + info{other.info}, + vtable{other.vtable}, + mode{other.mode} { + if(other.vtable) { + other.vtable(operation::move, other, this); + } + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + reset(); + + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + reset(); + + if(other.vtable) { + other.vtable(operation::move, other, this); + info = other.info; + vtable = other.vtable; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the object type if any, `type_id()` otherwise. + * @return The object type if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return *info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return vtable ? vtable(operation::get, *this, nullptr) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return *info == req ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @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) { + reset(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(basic_any &&other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + if(auto *val = other.data(); val) { + return (vtable(operation::transfer, *this, val) != nullptr); + } else { + return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); + } + } + + return false; + } + + /*! @brief Destroys contained object */ + void reset() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); + info = &type_id(); + vtable = nullptr; + mode = any_policy::owner; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return vtable != nullptr; + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + return basic_any{*this, any_policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::owner); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + any_policy mode; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &data) noexcept { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } else { + return any_cast(data); + } + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + const auto &info = type_id>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "../core/utility.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 + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/** + * @brief Service locator, nothing more. + * + * A service locator is 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 tiny class + * tries to fill the gap and to get rid of the burden of defining a different + * specific locator for each application. + * + * @note + * Users shouldn't retain references to a service. The recommended way is to + * retrieve the service implementation currently set each and every time the + * need for it arises. The risk is to incur in unexpected behaviors otherwise. + * + * @tparam Service Service type. + */ +template +class locator final { + class service_handle { + friend class locator; + std::shared_ptr value{}; + }; + +public: + /*! @brief Service type. */ + using type = Service; + /*! @brief Service node type. */ + using node_type = service_handle; + + /*! @brief Default constructor, deleted on purpose. */ + locator() = delete; + /*! @brief Default destructor, deleted on purpose. */ + ~locator() = delete; + + /** + * @brief Checks whether a service locator contains a value. + * @return True if the service locator contains a value, false otherwise. + */ + [[nodiscard]] static bool has_value() noexcept { + return (service != nullptr); + } + + /** + * @brief Returns a reference to a valid service, if any. + * + * @warning + * Invoking this function can result in undefined behavior if the service + * hasn't been set yet. + * + * @return A reference to the service currently set, if any. + */ + [[nodiscard]] static Service &value() noexcept { + ENTT_ASSERT(has_value(), "Service not available"); + return *service; + } + + /** + * @brief Returns a service if available or sets it from a fallback type. + * + * Arguments are used only if a service doesn't already exist. In all other + * cases, they are discarded. + * + * @tparam Args Types of arguments to use to construct the fallback service. + * @tparam Type Fallback service type. + * @param args Parameters to use to construct the fallback service. + * @return A reference to a valid service. + */ + template + [[nodiscard]] static Service &value_or(Args &&...args) { + return service ? *service : emplace(std::forward(args)...); + } + + /** + * @brief Sets or replaces a service. + * @tparam Type Service type. + * @tparam Args Types of arguments to use to construct the service. + * @param args Parameters to use to construct the service. + * @return A reference to a valid service. + */ + template + static Service &emplace(Args &&...args) { + service = std::make_shared(std::forward(args)...); + return *service; + } + + /** + * @brief Sets or replaces a service using a given allocator. + * @tparam Type Service type. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the service. + * @param alloc The allocator to use. + * @param args Parameters to use to construct the service. + * @return A reference to a valid service. + */ + template + static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) { + service = std::allocate_shared(alloc, std::forward(args)...); + return *service; + } + + /** + * @brief Returns a handle to the underlying service. + * @return A handle to the underlying service. + */ + static node_type handle() noexcept { + node_type node{}; + node.value = service; + return node; + } + + /** + * @brief Resets or replaces a service. + * @param other Optional handle with which to replace the service. + */ + static void reset(const node_type &other = {}) noexcept { + service = other.value; + } + + /** + * @brief Resets or replaces a service. + * @tparam Type Service type. + * @tparam Deleter Deleter type. + * @param elem A pointer to a service to manage. + * @param deleter A deleter to use to destroy the service. + */ + template> + static void reset(Type *elem, Deleter deleter = {}) { + service = std::shared_ptr{elem, std::move(deleter)}; + } + +private: + // std::shared_ptr because of its type erased allocator which is useful here + inline static std::shared_ptr service{}; +}; + +} // namespace entt + +#endif + +// #include "adl_pointer.hpp" +#ifndef ENTT_META_ADL_POINTER_HPP +#define ENTT_META_ADL_POINTER_HPP + +namespace entt { + +/** + * @brief ADL based lookup function for dereferencing meta pointer-like types. + * @tparam Type Element type. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +template +decltype(auto) dereference_meta_pointer_like(const Type &value) { + return *value; +} + +/** + * @brief Fake ADL based lookup function for meta pointer-like types. + * @tparam Type Element type. + */ +template +struct adl_meta_pointer_like { + /** + * @brief Uses the default ADL based lookup method to resolve the call. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ + static decltype(auto) dereference(const Type &value) { + return dereference_meta_pointer_like(value); + } +}; + +} // namespace entt + +#endif + +// #include "context.hpp" + +// #include "fwd.hpp" +#ifndef ENTT_META_FWD_HPP +#define ENTT_META_FWD_HPP + +namespace entt { + +class meta_sequence_container; + +class meta_associative_container; + +class meta_any; + +struct meta_handle; + +struct meta_prop; + +struct meta_data; + +struct meta_func; + +class meta_type; + +} // namespace entt + +#endif + +// #include "node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #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/enum.hpp" +#ifndef ENTT_CORE_ENUM_HPP +#define ENTT_CORE_ENUM_HPP + +#include + +namespace entt { + +/** + * @brief Enable bitmask support for enum classes. + * @tparam Type The enum type for which to enable bitmask support. + */ +template +struct enum_as_bitmask: std::false_type {}; + +/*! @copydoc enum_as_bitmask */ +template +struct enum_as_bitmask>: std::is_enum {}; + +/** + * @brief Helper variable template. + * @tparam Type The enum class type for which to enable bitmask support. + */ +template +inline constexpr bool enum_as_bitmask_v = enum_as_bitmask::value; + +} // namespace entt + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param lhs The first value to use. + * @param rhs The second value to use. + * @return The result of invoking the operator on the underlying types of the + * two values provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator|(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator&(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator^(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); +} + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param value The value to use. + * @return The result of invoking the operator on the underlying types of the + * value provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator~(const Type value) noexcept { + return static_cast(~static_cast>(value)); +} + +/*! @copydoc operator~ */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> +operator!(const Type value) noexcept { + return !static_cast>(value); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator|=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator&=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator^=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs ^ rhs)); +} + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "context.hpp" + +// #include "type_traits.hpp" +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + +#include +#include + +namespace entt { + +/** + * @brief Traits class template to be specialized to enable support for meta + * template information. + */ +template +struct meta_template_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * sequence containers. + */ +template +struct meta_sequence_container_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * associative containers. + */ +template +struct meta_associative_container_traits; + +/** + * @brief Provides the member constant `value` to true if a given type is a + * pointer-like type from the point of view of the meta system, false otherwise. + */ +template +struct is_meta_pointer_like: std::false_type {}; + +/** + * @brief Partial specialization to ensure that const pointer-like types are + * also accepted. + * @tparam Type Potentially pointer-like type. + */ +template +struct is_meta_pointer_like: is_meta_pointer_like {}; + +/** + * @brief Helper variable template. + * @tparam Type Potentially pointer-like type. + */ +template +inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value; + +} // namespace entt + +#endif + + +namespace entt { + +class meta_any; +class meta_type; +struct meta_handle; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class meta_traits : std::uint32_t { + is_none = 0x0000, + is_const = 0x0001, + is_static = 0x0002, + is_arithmetic = 0x0004, + is_integral = 0x0008, + is_signed = 0x0010, + is_array = 0x0020, + is_enum = 0x0040, + is_class = 0x0080, + is_meta_pointer_like = 0x0100, + is_meta_sequence_container = 0x0200, + is_meta_associative_container = 0x0400, + _entt_enum_as_bitmask +}; + +struct meta_type_node; + +struct meta_prop_node { + meta_type_node (*type)(const meta_context &) noexcept {}; + std::shared_ptr value{}; +}; + +struct meta_base_node { + meta_type_node (*type)(const meta_context &) noexcept {}; + const void *(*cast)(const void *) noexcept {}; +}; + +struct meta_conv_node { + meta_any (*conv)(const meta_ctx &, const void *){}; +}; + +struct meta_ctor_node { + using size_type = std::size_t; + + size_type arity{0u}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_any *const){}; +}; + +struct meta_dtor_node { + void (*dtor)(void *){}; +}; + +struct meta_data_node { + using size_type = std::size_t; + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + bool (*set)(meta_handle, meta_any){}; + meta_any (*get)(const meta_ctx &, meta_handle){}; + dense_map prop{}; +}; + +struct meta_func_node { + using size_type = std::size_t; + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*ret)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){}; + std::shared_ptr next{}; + dense_map prop{}; +}; + +struct meta_template_node { + using size_type = std::size_t; + + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type_node (*arg)(const meta_context &, const size_type) noexcept {}; +}; + +struct meta_type_descriptor { + dense_map ctor{}; + dense_map base{}; + dense_map conv{}; + dense_map data{}; + dense_map func{}; + dense_map prop{}; +}; + +struct meta_type_node { + using size_type = std::size_t; + + const type_info *info{}; + id_type id{}; + meta_traits traits{meta_traits::is_none}; + size_type size_of{0u}; + meta_type_node (*resolve)(const meta_context &) noexcept {}; + meta_type_node (*remove_pointer)(const meta_context &) noexcept {}; + meta_any (*default_constructor)(const meta_ctx &){}; + double (*conversion_helper)(void *, const void *){}; + meta_any (*from_void)(const meta_ctx &, void *, const void *){}; + meta_template_node templ{}; + meta_dtor_node dtor{}; + std::shared_ptr details{}; +}; + +template +auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) { + if(node.details) { + if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) { + return &it->second; + } + + for(auto &&curr: node.details->base) { + if(auto *elem = look_for(context, curr.second.type(context), id); elem) { + return elem; + } + } + } + + return static_cast*Member)>::mapped_type *>(nullptr); +} + +template +meta_type_node resolve(const meta_context &) noexcept; + +template +[[nodiscard]] auto meta_arg_node(const meta_context &context, type_list, [[maybe_unused]] const std::size_t index) noexcept { + [[maybe_unused]] std::size_t pos{}; + meta_type_node (*value)(const meta_context &) noexcept = nullptr; + ((value = (pos++ == index ? &resolve>> : value)), ...); + ENTT_ASSERT(value != nullptr, "Out of bounds"); + return value(context); +} + +[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept { + if(from.info && to.info && *from.info == *to.info) { + return instance; + } + + if(from.details) { + for(auto &&curr: from.details->base) { + if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) { + return elem; + } + } + } + + return nullptr; +} + +template +[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) { + if(from.info && *from.info == to) { + return func(instance, from); + } + + if(from.details) { + if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) { + return func(instance, it->second); + } + + for(auto &&curr: from.details->base) { + if(auto other = try_convert(context, curr.second.type(context), to, arithmetic_or_enum, curr.second.cast(instance), func); other) { + return other; + } + } + } + + if(from.conversion_helper && arithmetic_or_enum) { + return func(instance, from.conversion_helper); + } + + return func(instance); +} + +[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept { + const auto it = context.value.find(info.hash()); + return it != context.value.end() ? &it->second : nullptr; +} + +template +[[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept { + static_assert(std::is_same_v>>, "Invalid type"); + + if(auto *elem = try_resolve(context, type_id()); elem) { + return *elem; + } + + meta_type_node node{ + &type_id(), + type_id().hash(), + (std::is_arithmetic_v ? meta_traits::is_arithmetic : meta_traits::is_none) + | (std::is_integral_v ? meta_traits::is_integral : meta_traits::is_none) + | (std::is_signed_v ? meta_traits::is_signed : meta_traits::is_none) + | (std::is_array_v ? meta_traits::is_array : meta_traits::is_none) + | (std::is_enum_v ? meta_traits::is_enum : meta_traits::is_none) + | (std::is_class_v ? meta_traits::is_class : meta_traits::is_none) + | (is_meta_pointer_like_v ? meta_traits::is_meta_pointer_like : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_sequence_container : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_associative_container : meta_traits::is_none), + size_of_v, + &resolve, + &resolve>>}; + + if constexpr(std::is_default_constructible_v) { + node.default_constructor = +[](const meta_ctx &ctx) { + return meta_any{ctx, std::in_place_type}; + }; + } + + if constexpr(std::is_arithmetic_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(*static_cast(value))) : static_cast(*static_cast(value)); + }; + } else if constexpr(std::is_enum_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); + }; + } + + if constexpr(!std::is_void_v && !std::is_function_v) { + node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) { + if(element) { + return meta_any{ctx, std::in_place_type &>, *static_cast *>(element)}; + } + + return meta_any{ctx, std::in_place_type &>, *static_cast *>(as_const)}; + }; + } + + if constexpr(is_complete_v>) { + node.templ = meta_template_node{ + meta_template_traits::args_type::size, + &resolve::class_type>, + +[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits::args_type{}, index); }}; + } + + return node; +} + +} // namespace internal +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "context.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct meta_range_iterator final { + using difference_type = std::ptrdiff_t; + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr meta_range_iterator() noexcept + : it{}, + ctx{} {} + + constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept + : it{iter}, + ctx{&area} {} + + constexpr meta_range_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr meta_range_iterator operator++(int) noexcept { + meta_range_iterator orig = *this; + return ++(*this), orig; + } + + constexpr meta_range_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr meta_range_iterator operator--(int) noexcept { + meta_range_iterator orig = *this; + return operator--(), orig; + } + + constexpr meta_range_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr meta_range_iterator operator+(const difference_type value) const noexcept { + meta_range_iterator copy = *this; + return (copy += value); + } + + constexpr meta_range_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr meta_range_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, Type{*ctx, it[value].second}}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->first, Type{*ctx, it->second}}; + } + + template + friend constexpr std::ptrdiff_t operator-(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator==(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator<(const meta_range_iterator &, const meta_range_iterator &) noexcept; + +private: + It it; + const meta_ctx *ctx; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects returned. + * @tparam It Type of forward iterator. + */ +template +using meta_range = iterable_adaptor>; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; + +/*! @brief Proxy object for sequence containers. */ +class meta_sequence_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_sequence_container() noexcept + : meta_sequence_container{locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_sequence_container(const meta_ctx &area) noexcept + : ctx{&area} {} + + /** + * @brief Rebinds a proxy object to a sequence container type. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + void rebind(Type &instance) noexcept { + value_type_node = &internal::resolve; + const_reference_node = &internal::resolve>>; + size_fn = meta_sequence_container_traits>::size; + clear_fn = meta_sequence_container_traits>::clear; + reserve_fn = meta_sequence_container_traits>::reserve; + resize_fn = meta_sequence_container_traits>::resize; + begin_fn = meta_sequence_container_traits>::begin; + end_fn = meta_sequence_container_traits>::end; + insert_fn = meta_sequence_container_traits>::insert; + erase_fn = meta_sequence_container_traits>::erase; + const_only = std::is_const_v; + data = &instance; + } + + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool resize(const size_type); + inline bool clear(); + inline bool reserve(const size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline iterator insert(iterator, meta_any); + inline iterator erase(iterator); + [[nodiscard]] inline meta_any operator[](const size_type); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*const_reference_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + bool (*resize_fn)(void *, const size_type){}; + iterator (*begin_fn)(const meta_ctx &, void *, const void *){}; + iterator (*end_fn)(const meta_ctx &, void *, const void *){}; + iterator (*insert_fn)(const meta_ctx &, void *, const void *, const void *, const iterator &){}; + iterator (*erase_fn)(const meta_ctx &, void *, const iterator &){}; + const void *data{}; + bool const_only{}; +}; + +/*! @brief Proxy object for associative containers. */ +class meta_associative_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_associative_container() noexcept + : meta_associative_container{locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_associative_container(const meta_ctx &area) noexcept + : ctx{&area} {} + + /** + * @brief Rebinds a proxy object to an associative container type. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + void rebind(Type &instance) noexcept { + key_type_node = &internal::resolve; + value_type_node = &internal::resolve; + + if constexpr(!meta_associative_container_traits>::key_only) { + mapped_type_node = &internal::resolve; + } + + size_fn = &meta_associative_container_traits>::size; + clear_fn = &meta_associative_container_traits>::clear; + reserve_fn = &meta_associative_container_traits>::reserve; + begin_fn = &meta_associative_container_traits>::begin; + end_fn = &meta_associative_container_traits>::end; + insert_fn = &meta_associative_container_traits>::insert; + erase_fn = &meta_associative_container_traits>::erase; + find_fn = &meta_associative_container_traits>::find; + const_only = std::is_const_v; + data = &instance; + } + + [[nodiscard]] inline bool key_only() const noexcept; + [[nodiscard]] inline meta_type key_type() const noexcept; + [[nodiscard]] inline meta_type mapped_type() const noexcept; + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool clear(); + inline bool reserve(const size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline bool insert(meta_any, meta_any); + inline size_type erase(meta_any); + [[nodiscard]] inline iterator find(meta_any); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + internal::meta_type_node (*key_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + iterator (*begin_fn)(const meta_ctx &, void *, const void *){}; + iterator (*end_fn)(const meta_ctx &, void *, const void *){}; + bool (*insert_fn)(void *, const void *, const void *){}; + size_type (*erase_fn)(void *, const void *){}; + iterator (*find_fn)(const meta_ctx &, void *, const void *, const void *){}; + const void *data{}; + bool const_only{}; +}; + +/*! @brief Possible modes of a meta any object. */ +using meta_any_policy = any_policy; + +/*! @brief Opaque wrapper for values of any type. */ +class meta_any { + using vtable_type = void(const internal::meta_traits op, const bool, const void *, void *); + + template + static std::enable_if_t>, Type>> basic_vtable([[maybe_unused]] const internal::meta_traits req, [[maybe_unused]] const bool const_only, [[maybe_unused]] const void *value, [[maybe_unused]] void *other) { + if constexpr(is_meta_pointer_like_v) { + if(req == internal::meta_traits::is_meta_pointer_like) { + if constexpr(std::is_function_v::element_type>) { + static_cast(other)->emplace(*static_cast(value)); + } else if constexpr(!std::is_void_v::element_type>>) { + using in_place_type = decltype(adl_meta_pointer_like::dereference(*static_cast(value))); + + if constexpr(std::is_constructible_v) { + if(const auto &pointer_like = *static_cast(value); pointer_like) { + static_cast(other)->emplace(adl_meta_pointer_like::dereference(pointer_like)); + } + } else { + static_cast(other)->emplace(adl_meta_pointer_like::dereference(*static_cast(value))); + } + } + } + } + + if constexpr(is_complete_v>) { + if(req == internal::meta_traits::is_meta_sequence_container) { + const_only ? static_cast(other)->rebind(*static_cast(value)) : static_cast(other)->rebind(*static_cast(const_cast(value))); + } + } + + if constexpr(is_complete_v>) { + if(req == internal::meta_traits::is_meta_associative_container) { + const_only ? static_cast(other)->rebind(*static_cast(value)) : static_cast(other)->rebind(*static_cast(const_cast(value))); + } + } + } + + void release() { + if(node.dtor.dtor && (storage.policy() == any_policy::owner)) { + node.dtor.dtor(storage.data()); + } + } + + meta_any(const meta_ctx &area, const meta_any &other, any ref) noexcept + : storage{std::move(ref)}, + ctx{&area}, + node{storage ? other.node : internal::meta_type_node{}}, + vtable{storage ? other.vtable : &basic_vtable} {} + +public: + /*! Default constructor. */ + meta_any() noexcept + : meta_any{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_any(meta_ctx_arg_t, const meta_ctx &area) noexcept + : storage{}, + ctx{&area}, + node{}, + vtable{&basic_vtable} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @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, Args &&...args) + : meta_any{locator::value_or(), std::in_place_type, std::forward(args)...} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param area The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(const meta_ctx &area, std::in_place_type_t, Args &&...args) + : storage{std::in_place_type, std::forward(args)...}, + ctx{&area}, + node{internal::resolve>>(internal::meta_context::from(*ctx))}, + vtable{&basic_vtable>>} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(Type &&value) + : meta_any{locator::value_or(), std::forward(value)} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param area The context from which to search for meta types. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(const meta_ctx &area, Type &&value) + : meta_any{area, std::in_place_type>, std::forward(value)} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_any(const meta_ctx &area, const meta_any &other) + : meta_any{other} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_any(const meta_ctx &area, meta_any &&other) + : meta_any{std::move(other)} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + meta_any(const meta_any &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + meta_any(meta_any &&other) noexcept + : storage{std::move(other.storage)}, + ctx{other.ctx}, + node{std::exchange(other.node, internal::meta_type_node{})}, + vtable{std::exchange(other.vtable, &basic_vtable)} {} + + /*! @brief Frees the internal storage, whatever it means. */ + ~meta_any() { + release(); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This meta any object. + */ + meta_any &operator=(const meta_any &other) { + release(); + storage = other.storage; + ctx = other.ctx; + node = other.node; + vtable = other.vtable; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ + meta_any &operator=(meta_any &&other) noexcept { + release(); + storage = std::move(other.storage); + ctx = other.ctx; + node = std::exchange(other.node, internal::meta_type_node{}); + vtable = std::exchange(other.vtable, &basic_vtable); + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This meta any object. + */ + template + std::enable_if_t, meta_any>, meta_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /*! @copydoc any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /*! @copydoc any::data */ + [[nodiscard]] const void *data() const noexcept { + return storage.data(); + } + + /*! @copydoc any::data */ + [[nodiscard]] void *data() noexcept { + return storage.data(); + } + + /** + * @brief Invokes the underlying function, if possible. + * @tparam Args Types of arguments to use to invoke the function. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, Args &&...args) const; + + /*! @copydoc invoke */ + template + meta_any invoke(const id_type id, Args &&...args); + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, Type &&value); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id) const; + + /*! @copydoc get */ + [[nodiscard]] meta_any get(const id_type id); + + /** + * @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 + [[nodiscard]] const Type *try_cast() const { + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data())); + } + + /*! @copydoc try_cast */ + template + [[nodiscard]] Type *try_cast() { + if constexpr(std::is_const_v) { + return std::as_const(*this).try_cast>(); + } else { + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(const_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data()))); + } + } + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A reference to the contained instance. + */ + template + [[nodiscard]] Type cast() const { + auto *const instance = try_cast>(); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); + } + + /*! @copydoc cast */ + template + [[nodiscard]] Type cast() { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = try_cast>(); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ + [[nodiscard]] meta_any allow_cast(const meta_type &type) const; + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ + [[nodiscard]] bool allow_cast(const meta_type &type) { + if(auto other = std::as_const(*this).allow_cast(type); other) { + if((other.storage.policy() == any_policy::owner)) { + std::swap(*this, other); + } + + return true; + } + + return false; + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ + template + [[nodiscard]] meta_any allow_cast() const { + if constexpr(std::is_reference_v && !std::is_const_v>) { + return meta_any{meta_ctx_arg, *ctx}; + } else { + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}); + } + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ + template + [[nodiscard]] bool allow_cast() { + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}) && (!(std::is_reference_v && !std::is_const_v>) || storage.data() != nullptr); + } + + /*! @copydoc any::emplace */ + template + void emplace(Args &&...args) { + release(); + storage.emplace(std::forward(args)...); + node = internal::resolve>>(internal::meta_context::from(*ctx)); + vtable = &basic_vtable>>; + } + + /*! @copydoc any::assign */ + bool assign(const meta_any &other); + + /*! @copydoc any::assign */ + bool assign(meta_any &&other); + + /*! @copydoc any::reset */ + void reset() { + release(); + storage.reset(); + node = {}; + vtable = &basic_vtable; + } + + /** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ + [[nodiscard]] meta_sequence_container as_sequence_container() noexcept { + meta_sequence_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_sequence_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy); + return proxy; + } + + /*! @copydoc as_sequence_container */ + [[nodiscard]] meta_sequence_container as_sequence_container() const noexcept { + meta_sequence_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_sequence_container, true, data(), &proxy); + return proxy; + } + + /** + * @brief Returns an associative container proxy. + * @return An associative container proxy for the underlying object. + */ + [[nodiscard]] meta_associative_container as_associative_container() noexcept { + meta_associative_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_associative_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy); + return proxy; + } + + /*! @copydoc as_associative_container */ + [[nodiscard]] meta_associative_container as_associative_container() const noexcept { + meta_associative_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_associative_container, true, data(), &proxy); + return proxy; + } + + /** + * @brief Indirection operator for dereferencing opaque objects. + * @return A wrapper that shares a reference to an unmanaged object if the + * wrapped element is dereferenceable, an invalid meta any otherwise. + */ + [[nodiscard]] meta_any operator*() const noexcept { + meta_any ret{meta_ctx_arg, *ctx}; + vtable(internal::meta_traits::is_meta_pointer_like, true, storage.data(), &ret); + return ret; + } + + /** + * @brief Returns false if a wrapper is invalid, true otherwise. + * @return False if the wrapper is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(node.info == nullptr); + } + + /*! @copydoc any::operator== */ + [[nodiscard]] bool operator==(const meta_any &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info && storage == other.storage)); + } + + /*! @copydoc any::operator!= */ + [[nodiscard]] bool operator!=(const meta_any &other) const noexcept { + return !(*this == other); + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() const noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; + } + + /*! @copydoc any::owner */ + [[deprecated("use policy() and meta_any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (storage.policy() == any_policy::owner); + } + + /** + * @brief Returns the current mode of a meta any object. + * @return The current mode of the meta any object. + */ + [[nodiscard]] meta_any_policy policy() const noexcept { + return storage.policy(); + } + +private: + any storage; + const meta_ctx *ctx; + internal::meta_type_node node; + vtable_type *vtable; +}; + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @param ctx The context from which to search for meta types. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { + return meta_any{ctx, std::in_place_type, std::forward(value)}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(Type &&value) { + return forward_as_meta(locator::value_or(), std::forward(value)); +} + +/** + * @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. + */ +struct meta_handle { + /*! Default constructor. */ + meta_handle() noexcept + : meta_handle{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_handle(meta_ctx_arg_t, const meta_ctx &area) noexcept + : any{meta_ctx_arg, area} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(const meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param ctx The context from which to search for meta types. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(const meta_ctx &ctx, Type &value) noexcept + : any{ctx, std::in_place_type, value} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(Type &value) noexcept + : meta_handle{locator::value_or(), value} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_handle(const meta_ctx &area, const meta_handle &other) + : any{area, other.any} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_handle(const meta_ctx &area, meta_handle &&other) + : any{area, std::move(other.any)} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + meta_handle(const meta_handle &) = delete; + + /*! @brief Default move constructor. */ + meta_handle(meta_handle &&) = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This meta handle. + */ + meta_handle &operator=(const meta_handle &) = delete; + + /** + * @brief Default move assignment operator. + * @return This meta handle. + */ + meta_handle &operator=(meta_handle &&) = default; + + /** + * @brief Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(any); + } + + /*! @copydoc meta_any::operator== */ + [[nodiscard]] bool operator==(const meta_handle &other) const noexcept { + return (any == other.any); + } + + /*! @copydoc meta_any::operator!= */ + [[nodiscard]] bool operator!=(const meta_handle &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Access operator for accessing the contained opaque object. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any *operator->() { + return &any; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const meta_any *operator->() const { + return &any; + } + +private: + meta_any any; +}; + +/*! @brief Opaque wrapper for properties of any type. */ +struct meta_prop { + /*! @brief Default constructor. */ + meta_prop() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_prop(const meta_ctx &area, const internal::meta_prop_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the stored value by const reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() const { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns the stored value by reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, node->value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /** + * @brief Checks if two objects refer to the same type. + * @param other The object with which to compare. + * @return True if the objects refer to the same type, false otherwise. + */ + [[nodiscard]] bool operator==(const meta_prop &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_prop_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_prop &lhs, const meta_prop &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for data members. */ +struct meta_data { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_data_node::size_type; + + /*! @brief Default constructor. */ + meta_data() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the number of setters available. + * @return The number of setters available. + */ + [[nodiscard]] size_type arity() const noexcept { + return node->arity; + } + + /** + * @brief Indicates whether a data member is constant or not. + * @return True if the data member is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a data member is static or not. + * @return True if the data member is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); + } + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /** + * @brief Sets the value of a given variable. + * @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 && node->set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward(value)}); + } + + /** + * @brief Gets the value of a given variable. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(meta_handle instance) const { + return node->get(*ctx, meta_handle{*ctx, std::move(instance)}); + } + + /** + * @brief Returns the type accepted by the i-th setter. + * @param index Index of the setter of which to return the accepted type. + * @return The type accepted by the i-th setter. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; + + /** + * @brief Returns a range to visit registered meta properties. + * @return An iterable range to visit registered meta properties. + */ + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; + } + + /** + * @brief Lookup utility for meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_data &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_data_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for member functions. */ +struct meta_func { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_func_node::size_type; + + /*! @brief Default constructor. */ + meta_func() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the number of arguments accepted by a member function. + * @return The number of arguments accepted by the member function. + */ + [[nodiscard]] size_type arity() const noexcept { + return node->arity; + } + + /** + * @brief Indicates whether a member function is constant or not. + * @return True if the member function is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a member function is static or not. + * @return True if the member function is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); + } + + /** + * @brief Returns the return type of a member function. + * @return The return type of the member function. + */ + [[nodiscard]] inline meta_type ret() const noexcept; + + /** + * @brief Returns the type of the i-th argument of a member function. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a member function. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; + + /** + * @brief Invokes the underlying function, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { + return sz == arity() ? node->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @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 wrapper containing the returned value, if any. + */ + template + meta_any invoke(meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(std::move(instance), arguments, sizeof...(Args)); + } + + /*! @copydoc meta_data::prop */ + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; + } + + /** + * @brief Lookup utility for meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } + + /** + * @brief Returns the next overload of a given function, if any. + * @return The next overload of the given function, if any. + */ + [[nodiscard]] meta_func next() const { + return node->next ? meta_func{*ctx, *node->next} : meta_func{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_func &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_func_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for types. */ +class meta_type { + template + [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const { + decltype(next()) candidate = nullptr; + size_type same{}; + bool ambiguous{}; + + for(auto curr = next(); curr; curr = next()) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(constness && !static_cast(curr->traits & internal::meta_traits::is_const)) { + continue; + } + } + + if(curr->arity == sz) { + size_type match{}; + size_type pos{}; + + for(; pos < sz && args[pos]; ++pos) { + const auto other = curr->arg(*ctx, pos); + const auto type = args[pos].type(); + + if(const auto &info = other.info(); info == type.info()) { + ++match; + } else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash()))) || (type.node.conversion_helper && other.node.conversion_helper))) { + break; + } + } + + if(pos == sz) { + if(!candidate || match > same) { + candidate = curr; + same = match; + ambiguous = false; + } else if(match == same) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(static_cast(curr->traits & internal::meta_traits::is_const) != static_cast(candidate->traits & internal::meta_traits::is_const)) { + candidate = static_cast(candidate->traits & internal::meta_traits::is_const) ? curr : candidate; + ambiguous = false; + continue; + } + } + + ambiguous = true; + } + } + } + } + + return ambiguous ? nullptr : candidate; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_type_node::size_type; + + /*! @brief Default constructor. */ + meta_type() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept + : node{curr}, + ctx{&area} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept + : meta_type{area, curr.type(internal::meta_context::from(area))} {} + + /** + * @brief Returns the type info object of the underlying type. + * @return The type info object of the underlying type. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *node.info; + } + + /** + * @brief Returns the identifier assigned to a type. + * @return The identifier assigned to the type. + */ + [[nodiscard]] id_type id() const noexcept { + return node.id; + } + + /** + * @brief Returns the size of the underlying type if known. + * @return The size of the underlying type if known, 0 otherwise. + */ + [[nodiscard]] size_type size_of() const noexcept { + return node.size_of; + } + + /** + * @brief Checks whether a type refers to an arithmetic type or not. + * @return True if the underlying type is an arithmetic type, false + * otherwise. + */ + [[nodiscard]] bool is_arithmetic() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_arithmetic); + } + + /** + * @brief Checks whether a type refers to an integral type or not. + * @return True if the underlying type is an integral type, false otherwise. + */ + [[nodiscard]] bool is_integral() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_integral); + } + + /** + * @brief Checks whether a type refers to a signed type or not. + * @return True if the underlying type is a signed type, false otherwise. + */ + [[nodiscard]] bool is_signed() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_signed); + } + + /** + * @brief Checks whether a type refers to an array type or not. + * @return True if the underlying type is an array type, false otherwise. + */ + [[nodiscard]] bool is_array() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_array); + } + + /** + * @brief Checks whether a type refers to an enum or not. + * @return True if the underlying type is an enum, false otherwise. + */ + [[nodiscard]] bool is_enum() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_enum); + } + + /** + * @brief Checks whether a type refers to a class or not. + * @return True if the underlying type is a class, false otherwise. + */ + [[nodiscard]] bool is_class() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_class); + } + + /** + * @brief Checks whether a type refers to a pointer or not. + * @return True if the underlying type is a pointer, false otherwise. + */ + [[nodiscard]] bool is_pointer() const noexcept { + return node.info && (node.info->hash() != remove_pointer().info().hash()); + } + + /** + * @brief Provides the type for which the pointer is defined. + * @return The type for which the pointer is defined or this type if it + * doesn't refer to a pointer type. + */ + [[nodiscard]] meta_type remove_pointer() const noexcept { + return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; // NOLINT + } + + /** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is pointer-like, false otherwise. + */ + [[nodiscard]] bool is_pointer_like() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_pointer_like); + } + + /** + * @brief Checks whether a type refers to a sequence container or not. + * @return True if the type is a sequence container, false otherwise. + */ + [[nodiscard]] bool is_sequence_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_sequence_container); + } + + /** + * @brief Checks whether a type refers to an associative container or not. + * @return True if the type is an associative container, false otherwise. + */ + [[nodiscard]] bool is_associative_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_associative_container); + } + + /** + * @brief Checks whether a type refers to a recognized class template + * specialization or not. + * @return True if the type is a recognized class template specialization, + * false otherwise. + */ + [[nodiscard]] bool is_template_specialization() const noexcept { + return (node.templ.arity != 0u); + } + + /** + * @brief Returns the number of template arguments. + * @return The number of template arguments. + */ + [[nodiscard]] size_type template_arity() const noexcept { + return node.templ.arity; + } + + /** + * @brief Returns a tag for the class template of the underlying type. + * @return The tag for the class template of the underlying type. + */ + [[nodiscard]] inline meta_type template_type() const noexcept { + return node.templ.type ? meta_type{*ctx, node.templ.type(internal::meta_context::from(*ctx))} : meta_type{}; + } + + /** + * @brief Returns the type of the i-th template argument of a type. + * @param index Index of the template argument of which to return the type. + * @return The type of the i-th template argument of a type. + */ + [[nodiscard]] inline meta_type template_arg(const size_type index) const noexcept { + return index < template_arity() ? meta_type{*ctx, node.templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{}; + } + + /** + * @brief Checks if a type supports direct casting to another type. + * @param other The meta type to test for. + * @return True if direct casting is allowed, false otherwise. + */ + [[nodiscard]] bool can_cast(const meta_type &other) const noexcept { + // casting this is UB in all cases but we aren't going to use the resulting pointer, so... + return (internal::try_cast(internal::meta_context::from(*ctx), node, other.node, this) != nullptr); + } + + /** + * @brief Checks if a type supports conversion it to another type. + * @param other The meta type to test for. + * @return True if the conversion is allowed, false otherwise. + */ + [[nodiscard]] bool can_convert(const meta_type &other) const noexcept { + return (internal::try_convert(internal::meta_context::from(*ctx), node, other.info(), other.is_arithmetic() || other.is_enum(), nullptr, [](const void *, auto &&...args) { return ((static_cast(args), 1) + ... + 0u); }) != 0u); + } + + /** + * @brief Returns a range to visit registered top-level base meta types. + * @return An iterable range to visit registered top-level base meta types. + */ + [[nodiscard]] meta_range base() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->base.cbegin()}, {*ctx, node.details->base.cend()}} : range_type{}; + } + + /** + * @brief Returns a range to visit registered top-level meta data. + * @return An iterable range to visit registered top-level meta data. + */ + [[nodiscard]] meta_range data() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->data.cbegin()}, {*ctx, node.details->data.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta data (bases are also visited). + * @param id Unique identifier. + * @return The registered meta data for the given identifier, if any. + */ + [[nodiscard]] meta_data data(const id_type id) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), node, id); + return elem ? meta_data{*ctx, *elem} : meta_data{}; + } + + /** + * @brief Returns a range to visit registered top-level functions. + * @return An iterable range to visit registered top-level functions. + */ + [[nodiscard]] meta_range func() const noexcept { + using return_type = meta_range; + return node.details ? return_type{{*ctx, node.details->func.cbegin()}, {*ctx, node.details->func.cend()}} : return_type{}; + } + + /** + * @brief Lookup utility for meta functions (bases are also visited). + * + * In case of overloaded functions, a random one is returned. + * + * @param id Unique identifier. + * @return The registered meta function for the given identifier, if any. + */ + [[nodiscard]] meta_func func(const id_type id) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), node, id); + return elem ? meta_func{*ctx, *elem} : meta_func{}; + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { + if(node.details) { + if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &(first++)->second; }); candidate) { + return candidate->invoke(*ctx, args); + } + } + + if(sz == 0u && node.default_constructor) { + return node.default_constructor(*ctx); + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief construct + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + template + [[nodiscard]] meta_any construct(Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return construct(arguments, sizeof...(Args)); + } + + /** + * @brief Wraps an opaque element of the underlying type. + * @param element A valid pointer to an element of the underlying type. + * @return A wrapper that references the given instance. + */ + [[nodiscard]] meta_any from_void(void *element) const { + return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /*! @copydoc from_void */ + [[nodiscard]] meta_any from_void(const void *element) const { + return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Invokes a function given an identifier, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { + if(node.details) { + if(auto it = node.details->func.find(id); it != node.details->func.cend()) { + if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { + return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); + } + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.invoke(id, *instance.operator->(), args, sz); elem) { + return elem; + } + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @param id Unique identifier. + * @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 wrapper containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(id, std::move(instance), arguments, sizeof...(Args)); + } + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @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(const id_type id, meta_handle instance, Type &&value) const { + const auto candidate = data(id); + return candidate && candidate.set(std::move(instance), std::forward(value)); + } + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { + const auto candidate = data(id); + return candidate ? candidate.get(std::move(instance)) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns a range to visit registered top-level meta properties. + * @return An iterable range to visit registered top-level meta properties. + */ + [[nodiscard]] meta_range prop() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->prop.cbegin()}, {*ctx, node.details->prop.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta properties (bases are also visited). + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::prop>(internal::meta_context::from(*ctx), node, key); + return elem ? meta_prop{*ctx, *elem} : meta_prop{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(ctx == nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_type &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info)); + } + +private: + internal::meta_type_node node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept { + return !(lhs == rhs); +} + +[[nodiscard]] inline meta_type meta_any::type() const noexcept { + return node.info ? meta_type{*ctx, node} : meta_type{}; +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) const { + return type().invoke(id, *this, std::forward(args)...); +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) { + return type().invoke(id, *this, std::forward(args)...); +} + +template +bool meta_any::set(const id_type id, Type &&value) { + return type().set(id, *this, std::forward(value)); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) const { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { + return internal::try_convert(internal::meta_context::from(*ctx), node, type.info(), type.is_arithmetic() || type.is_enum(), data(), [this, &type]([[maybe_unused]] const void *instance, auto &&...args) { + if constexpr((std::is_same_v>, internal::meta_type_node> || ...)) { + return (args.from_void(*ctx, nullptr, instance), ...); + } else if constexpr((std::is_same_v>, internal::meta_conv_node> || ...)) { + return (args.conv(*ctx, instance), ...); + } else if constexpr((std::is_same_v>, decltype(internal::meta_type_node::conversion_helper)> || ...)) { + // exploits the fact that arithmetic types and enums are also default constructible + auto other = type.construct(); + const auto value = (args(nullptr, instance), ...); + other.node.conversion_helper(other.data(), &value); + return other; + } else { + // forwards to force a compile-time error in case of available arguments + return meta_any{meta_ctx_arg, *ctx, std::forward(args)...}; + } + }); +} + +inline bool meta_any::assign(const meta_any &other) { + auto value = other.allow_cast({*ctx, node}); + return value && storage.assign(value.storage); +} + +inline bool meta_any::assign(meta_any &&other) { + if(*node.info == *other.node.info) { + return storage.assign(std::move(other.storage)); + } + + return assign(std::as_const(other)); +} + +[[nodiscard]] inline meta_type meta_data::type() const noexcept { + return meta_type{*ctx, node->type(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_func::ret() const noexcept { + return meta_type{*ctx, node->ret(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +class meta_sequence_container::meta_iterator final { + using vtable_type = void(const void *, const std::ptrdiff_t, meta_any *); + + template + static void basic_vtable(const void *value, const std::ptrdiff_t offset, meta_any *other) { + const auto &it = *static_cast(value); + other ? other->emplace(*it) : std::advance(const_cast(it), offset); + } + +public: + using difference_type = std::ptrdiff_t; + using value_type = meta_any; + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::bidirectional_iterator_tag; + + meta_iterator() noexcept + : meta_iterator{locator::value_or()} {} + + meta_iterator(const meta_ctx &area) noexcept + : ctx{&area} {} + + template + meta_iterator(const meta_ctx &area, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + vtable(handle.data(), 1, nullptr); + return *this; + } + + meta_iterator operator++(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), ++value, nullptr); + return orig; + } + + meta_iterator &operator--() noexcept { + vtable(handle.data(), -1, nullptr); + return *this; + } + + meta_iterator operator--(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), --value, nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{meta_ctx_arg, *ctx}; + vtable(handle.data(), 0, &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { + return !(*this == other); + } + + [[nodiscard]] const any &base() const noexcept { + return handle; + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; + +class meta_associative_container::meta_iterator final { + using vtable_type = void(const void *, std::pair *); + + template + static void basic_vtable(const void *value, std::pair *other) { + if(const auto &it = *static_cast(value); other) { + if constexpr(KeyOnly) { + other->first.emplace(*it); + } else { + other->first.emplacefirst))>(it->first); + other->second.emplacesecond))>(it->second); + } + } else { + ++const_cast(it); + } + } + +public: + using difference_type = std::ptrdiff_t; + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + meta_iterator() noexcept + : meta_iterator{locator::value_or()} {} + + meta_iterator(const meta_ctx &area) noexcept + : ctx{&area} {} + + template + meta_iterator(const meta_ctx &area, std::bool_constant, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + vtable(handle.data(), nullptr); + return *this; + } + + meta_iterator operator++(int) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}}; + vtable(handle.data(), &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { + return !(*this == other); + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; +/*! @endcond */ + +/** + * @brief Returns the meta value type of a container. + * @return The meta value type of the container. + */ +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @brief Returns the size of a container. + * @return The size of the container. + */ +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const noexcept { + return size_fn(data); +} + +/** + * @brief Resizes a container to contain a given number of elements. + * @param sz The new size of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::resize(const size_type sz) { + return !const_only && resize_fn(const_cast(data), sz); +} + +/** + * @brief Clears the content of a container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/** + * @brief Reserves storage for at least the given number of elements. + * @param sz The new capacity of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/** + * @brief Returns an iterator to the first element of a container. + * @return An iterator to the first element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { + return begin_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @brief Returns an iterator that is past the last element of a container. + * @return An iterator that is past the last element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { + return end_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @brief Inserts an element at a specified location of a container. + * @param it Iterator before which the element will be inserted. + * @param value Element value to insert. + * @return A possibly invalid iterator to the inserted element. + */ +inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) { + // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector + if(const auto vtype = value_type_node(internal::meta_context::from(*ctx)); !const_only && (value.allow_cast({*ctx, vtype}) || value.allow_cast({*ctx, const_reference_node(internal::meta_context::from(*ctx))}))) { + const bool is_value_type = (value.type().info() == *vtype.info); + return insert_fn(*ctx, const_cast(data), is_value_type ? std::as_const(value).data() : nullptr, is_value_type ? nullptr : std::as_const(value).data(), it); + } + + return iterator{*ctx}; +} + +/** + * @brief Removes a given element from a container. + * @param it Iterator to the element to remove. + * @return A possibly invalid iterator following the last removed element. + */ +inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) { + return const_only ? iterator{*ctx} : erase_fn(*ctx, const_cast(data), it); +} + +/** + * @brief Returns a reference to the element at a given location of a container + * (no bounds checking is performed). + * @param pos The position of the element to return. + * @return A reference to the requested element properly wrapped. + */ +[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) { + auto it = begin(); + it.operator++(static_cast(pos) - 1); + return *it; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_sequence_container::operator bool() const noexcept { + return (data != nullptr); +} + +/** + * @brief Returns true if a container is also key-only, false otherwise. + * @return True if the associative container is also key-only, false otherwise. + */ +[[deprecated("use mapped_type() instead")]] [[nodiscard]] inline bool meta_associative_container::key_only() const noexcept { + return (mapped_type_node == nullptr); +} + +/** + * @brief Returns the meta key type of a container. + * @return The meta key type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept { + return key_type_node ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @brief Returns the meta mapped type of a container. + * @return The meta mapped type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept { + return mapped_type_node ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept { + return size_fn(data); +} + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/*! @copydoc meta_sequence_container::reserve */ +inline bool meta_associative_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { + return begin_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { + return end_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @brief Inserts a key-only or key/value element into a container. + * @param key The key of the element to insert. + * @param value The value of the element to insert, if needed. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { + return !const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) + && (!mapped_type_node || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))})) + && insert_fn(const_cast(data), std::as_const(key).data(), std::as_const(value).data()); +} + +/** + * @brief Removes the specified element from a container. + * @param key The key of the element to remove. + * @return A bool denoting whether the removal took place. + */ +inline meta_associative_container::size_type meta_associative_container::erase(meta_any key) { + return (!const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})) ? erase_fn(const_cast(data), std::as_const(key).data()) : 0u; +} + +/** + * @brief Returns an iterator to the element with a given key, if any. + * @param key The key of the element to search. + * @return An iterator to the element with the given key, if any. + */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { + return key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast(data), data, std::as_const(key).data()) : iterator{*ctx}; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_associative_container::operator bool() const noexcept { + return (data != nullptr); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct fixed_size_sequence_container: std::true_type {}; + +template +struct fixed_size_sequence_container>: std::false_type {}; + +template +inline constexpr bool fixed_size_sequence_container_v = fixed_size_sequence_container::value; + +template +struct key_only_associative_container: std::true_type {}; + +template +struct key_only_associative_container>: std::false_type {}; + +template +inline constexpr bool key_only_associative_container_v = key_only_associative_container::value; + +template +struct reserve_aware_container: std::false_type {}; + +template +struct reserve_aware_container>: std::true_type {}; + +template +inline constexpr bool reserve_aware_container_v = reserve_aware_container::value; + +} // namespace internal +/*! @endcond */ + +/** + * @brief General purpose implementation of meta sequence container traits. + * @tparam Type Type of underlying sequence container. + */ +template +struct basic_meta_sequence_container_traits { + static_assert(std::is_same_v>>, "Unexpected type"); + + /*! @brief True in case of key-only containers, false otherwise. */ + static constexpr bool fixed_size = internal::fixed_size_sequence_container_v; + + /*! @brief Unsigned integer type. */ + using size_type = typename meta_sequence_container::size_type; + /*! @brief Meta iterator type. */ + using iterator = typename meta_sequence_container::iterator; + + /** + * @brief Returns the number of elements in a container. + * @param container Opaque pointer to a container of the given type. + * @return Number of elements. + */ + [[nodiscard]] static size_type size(const void *container) { + return static_cast(container)->size(); + } + + /** + * @brief Clears a container. + * @param container Opaque pointer to a container of the given type. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool clear([[maybe_unused]] void *container) { + if constexpr(fixed_size) { + return false; + } else { + static_cast(container)->clear(); + return true; + } + } + + /** + * @brief Increases the capacity of a container. + * @param container Opaque pointer to a container of the given type. + * @param sz Desired capacity. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr(internal::reserve_aware_container_v) { + static_cast(container)->reserve(sz); + return true; + } else { + return false; + } + } + + /** + * @brief Resizes a container. + * @param container Opaque pointer to a container of the given type. + * @param sz The new number of elements. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool resize([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr(fixed_size || !std::is_default_constructible_v) { + return false; + } else { + static_cast(container)->resize(sz); + return true; + } + } + + /** + * @brief Returns a possibly const iterator to the beginning. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator to the first element of the container. + */ + static iterator begin(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, static_cast(container)->begin()} + : iterator{area, static_cast(as_const)->begin()}; + } + + /** + * @brief Returns a possibly const iterator to the end. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator that is past the last element of the container. + */ + static iterator end(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, static_cast(container)->end()} + : iterator{area, static_cast(as_const)->end()}; + } + + /** + * @brief Assigns one element to a container and constructs its object from + * a given opaque instance. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param value Optional opaque instance of the object to construct (as + * value type). + * @param cref Optional opaque instance of the object to construct (as + * decayed const reference type). + * @param it Iterator before which the element will be inserted. + * @return A possibly invalid iterator to the inserted element. + */ + [[nodiscard]] static iterator insert(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) { + if constexpr(fixed_size) { + return iterator{area}; + } else { + auto *const non_const = any_cast(&it.base()); + return {area, static_cast(container)->insert( + non_const ? *non_const : any_cast(it.base()), + value ? *static_cast(value) : *static_cast *>(cref))}; + } + } + + /** + * @brief Erases an element from a container. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param it An opaque iterator to the element to erase. + * @return A possibly invalid iterator following the last removed element. + */ + [[nodiscard]] static iterator erase(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) { + if constexpr(fixed_size) { + return iterator{area}; + } else { + auto *const non_const = any_cast(&it.base()); + return {area, static_cast(container)->erase(non_const ? *non_const : any_cast(it.base()))}; + } + } +}; + +/** + * @brief General purpose implementation of meta associative container traits. + * @tparam Type Type of underlying associative container. + */ +template +struct basic_meta_associative_container_traits { + static_assert(std::is_same_v>>, "Unexpected type"); + + /*! @brief True in case of key-only containers, false otherwise. */ + static constexpr bool key_only = internal::key_only_associative_container_v; + + /*! @brief Unsigned integer type. */ + using size_type = typename meta_associative_container::size_type; + /*! @brief Meta iterator type. */ + using iterator = typename meta_associative_container::iterator; + + /** + * @brief Returns the number of elements in a container. + * @param container Opaque pointer to a container of the given type. + * @return Number of elements. + */ + [[nodiscard]] static size_type size(const void *container) { + return static_cast(container)->size(); + } + + /** + * @brief Clears a container. + * @param container Opaque pointer to a container of the given type. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool clear(void *container) { + static_cast(container)->clear(); + return true; + } + + /** + * @brief Increases the capacity of a container. + * @param container Opaque pointer to a container of the given type. + * @param sz Desired capacity. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr(internal::reserve_aware_container_v) { + static_cast(container)->reserve(sz); + return true; + } else { + return false; + } + } + + /** + * @brief Returns a possibly const iterator to the beginning. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator to the first element of the container. + */ + static iterator begin(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, std::bool_constant{}, static_cast(container)->begin()} + : iterator{area, std::bool_constant{}, static_cast(as_const)->begin()}; + } + + /** + * @brief Returns a possibly const iterator to the end. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator that is past the last element of the container. + */ + static iterator end(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, std::bool_constant{}, static_cast(container)->end()} + : iterator{area, std::bool_constant{}, static_cast(as_const)->end()}; + } + + /** + * @brief Inserts an element into a container, if the key does not exist. + * @param container Opaque pointer to a container of the given type. + * @param key An opaque key value of an element to insert. + * @param value Optional opaque value to insert (key-value containers). + * @return True if the insertion took place, false otherwise. + */ + [[nodiscard]] static bool insert(void *container, const void *key, [[maybe_unused]] const void *value) { + if constexpr(key_only) { + return static_cast(container)->insert(*static_cast(key)).second; + } else { + return static_cast(container)->emplace(*static_cast(key), *static_cast(value)).second; + } + } + + /** + * @brief Removes an element from a container. + * @param container Opaque pointer to a container of the given type. + * @param key An opaque key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + [[nodiscard]] static size_type erase(void *container, const void *key) { + return static_cast(container)->erase(*static_cast(key)); + } + + /** + * @brief Finds an element with a given key. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @param key Opaque key value of an element to search for. + * @return An iterator to the element with the given key, if any. + */ + static iterator find(const meta_ctx &area, void *container, const void *as_const, const void *key) { + return container ? iterator{area, std::bool_constant{}, static_cast(container)->find(*static_cast(key))} + : iterator{area, std::bool_constant{}, static_cast(as_const)->find(*static_cast(key))}; + } +}; + +/** + * @brief Meta sequence container traits for `std::vector`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta sequence container traits for `std::array`s of any type. + * @tparam Type Template arguments for the container. + * @tparam N Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta sequence container traits for `std::list`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta sequence container traits for `std::deque`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::map`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::unordered_map`s of any + * type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::set`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::unordered_set`s of any + * type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `dense_map`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `dense_set`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +} // namespace entt + +#endif + +// #include "meta/context.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +// #include "../container/dense_map.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/utility.hpp" + + +namespace entt { + +class meta_ctx; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct meta_type_node; + +struct meta_context { + dense_map value{}; + + [[nodiscard]] inline static meta_context &from(meta_ctx &ctx); + [[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx); +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Disambiguation tag for constructors and the like. */ +class meta_ctx_arg_t final {}; + +/*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */ +inline constexpr meta_ctx_arg_t meta_ctx_arg{}; + +/*! @brief Opaque meta context type. */ +class meta_ctx: private internal::meta_context { + // attorney idiom like model to access the base class + friend struct internal::meta_context; +}; + +/*! @cond TURN_OFF_DOXYGEN */ +[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { + return ctx; +} + +[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { + return ctx; +} +/*! @endcond */ + +} // namespace entt + +#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" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../locator/locator.hpp" + +// #include "context.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + +#include + +namespace entt { + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v && !std::is_const_v>; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_is_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/** + * @brief Provides the member constant `value` to true if a type also is a meta + * policy, false otherwise. + * @tparam Type Type to check. + */ +template +struct is_meta_policy + : std::bool_constant || std::is_same_v || std::is_same_v || std::is_same_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type Type to check. + */ +template +inline constexpr bool is_meta_policy_v = is_meta_policy::value; + +} // namespace entt + +#endif + +// #include "range.hpp" + +// #include "resolve.hpp" +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + +#include +// #include "../core/type_info.hpp" + +// #include "../locator/locator.hpp" + +// #include "context.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + + +namespace entt { + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @param ctx The context from which to search for meta types. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::resolve>>(context)}; +} + +/** + * @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 +[[nodiscard]] meta_type resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns a range to use to visit all meta types. + * @param ctx The context from which to search for meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}}; +} + +/** + * @brief Returns a range to use to visit all meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param ctx The context from which to search for meta types. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept { + for(auto &&curr: resolve(ctx)) { + if(curr.second.id() == id) { + return curr.second; + } + } + + return meta_type{}; +} + +/** + * @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. + */ +[[nodiscard]] inline meta_type resolve(const id_type id) noexcept { + return resolve(locator::value_or(), id); +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param ctx The context from which to search for meta types. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept { + auto &&context = internal::meta_context::from(ctx); + const auto *elem = internal::try_resolve(context, info); + return elem ? meta_type{ctx, *elem} : meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const type_info &info) noexcept { + return resolve(locator::value_or(), info); +} + +} // namespace entt + +#endif + +// #include "utility.hpp" +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + +#include +#include +#include +#include +// #include "../core/type_traits.hpp" + +// #include "../locator/locator.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" + + +namespace entt { + +/** + * @brief Meta function descriptor traits. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + * @tparam Static Function staticness. + * @tparam Const Function constness. + */ +template +struct meta_function_descriptor_traits { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = Args; + + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr bool is_static = Static; + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr bool is_const = Const; +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct meta_function_descriptor; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + true> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta data is associated. + * @tparam Class Actual owner of the data member. + * @tparam Ret Data member type. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret &, + std::conditional_t, type_list<>, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam MaybeType First function argument. + * @tparam Args Other function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t< + std::is_same_v>, Type> || std::is_base_of_v>, Type>, + type_list, + type_list>, + !(std::is_same_v>, Type> || std::is_base_of_v>, Type>), + std::is_const_v> && (std::is_same_v>, Type> || std::is_base_of_v>, Type>)> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + type_list<>, + true, + false> {}; + +/** + * @brief Meta function helper. + * + * Converts a function type to be associated with a reflected type into its meta + * function descriptor. + * + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +class meta_function_helper { + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...) const); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret Class::*); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Class); + +public: + /*! @brief The meta function descriptor of the given function. */ + using type = decltype(get_rid_of_noexcept(std::declval())); +}; + +/** + * @brief Helper type. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +using meta_function_helper_t = typename meta_function_helper::type; + +/** + * @brief Wraps a value depending on the given policy. + * + * This function always returns a wrapped value in the requested context.
+ * Therefore, if the passed value is itself a wrapped object with a different + * context, it undergoes a rebinding to the requested context. + * + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param ctx The context from which to search for meta types. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { + if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type, value}; + } else if constexpr(std::is_same_v) { + static_assert(std::is_lvalue_reference_v, "Invalid type"); + return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; + } else { + return meta_any{ctx, std::forward(value)}; + } +} + +/** + * @brief Wraps a value depending on the given policy. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(Type &&value) { + return meta_dispatch(locator::value_or(), std::forward(value)); +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param ctx The context from which to search for meta types. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::meta_arg_node(context, Type{}, index)}; +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept { + return meta_arg(locator::value_or(), index); +} + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { + if constexpr(!std::is_same_v && !std::is_same_v) { + if constexpr(std::is_member_function_pointer_v || std::is_function_v>>) { + using descriptor = meta_function_helper_t; + using data_type = type_list_element_t; + + if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { + std::invoke(Data, *clazz, value.cast()); + return true; + } + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_reference_t::return_type>; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { + std::invoke(Data, *clazz) = value.cast(); + return true; + } + } + } else { + using data_type = std::remove_reference_t; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(value.allow_cast()) { + *Data = value.cast(); + return true; + } + } + } + } + + return false; +} + +/** + * @brief Gets the value of a given variable. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) { + if constexpr(std::is_member_pointer_v || std::is_function_v>>) { + if constexpr(!std::is_array_v>>>) { + if constexpr(std::is_invocable_v) { + if(auto *clazz = instance->try_cast(); clazz) { + return meta_dispatch(ctx, std::invoke(Data, *clazz)); + } + } + + if constexpr(std::is_invocable_v) { + if(auto *fallback = instance->try_cast(); fallback) { + return meta_dispatch(ctx, std::invoke(Data, *fallback)); + } + } + } + + return meta_any{meta_ctx_arg, ctx}; + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{meta_ctx_arg, ctx}; + } else { + return meta_dispatch(ctx, *Data); + } + } else { + return meta_dispatch(ctx, Data); + } +} + +/** + * @brief Gets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(meta_handle instance) { + return meta_getter(locator::value_or(), std::move(instance)); +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { + if constexpr(std::is_void_v(candidate), args...))>) { + std::invoke(std::forward(candidate), args...); + return meta_any{ctx, std::in_place_type}; + } else { + return meta_dispatch(ctx, std::invoke(std::forward(candidate), args...)); + } +} + +template +[[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence) { + using descriptor = meta_function_helper_t>; + + if constexpr(std::is_invocable_v, const Type &, type_list_element_t...>) { + if(const auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else if constexpr(std::is_invocable_v, Type &, type_list_element_t...>) { + if(auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { // NOLINT + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else { + if(((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(ctx, std::forward(candidate), (args + Index)->cast>()...); + } + } + + return meta_any{meta_ctx_arg, ctx}; +} + +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence) { + if(((args + Index)->allow_cast() && ...)) { + return meta_any{ctx, std::in_place_type, (args + Index)->cast()...}; + } + + return meta_any{meta_ctx_arg, ctx}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), std::forward(candidate), args); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args) { + return internal::meta_construct(ctx, args, std::index_sequence_for{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param ctx The context from which to search for meta types. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { + if constexpr(meta_function_helper_t::is_static || std::is_class_v>>) { + return internal::meta_invoke(ctx, {}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); + } else { + return internal::meta_invoke(ctx, *args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); + } +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { + return meta_construct(locator::value_or(), std::forward(candidate), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { + return meta_construct(ctx, Candidate, args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +[[nodiscard]] inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) { + auto &&context = internal::meta_context::from(ctx); + ENTT_ASSERT(context.value.contains(info.hash()), "Type not available"); + return context.value[info.hash()]; +} + +inline meta_data_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_data_node node) { + return parent.details->data.insert_or_assign(id, std::move(node)).first->second; +} + +inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_func_node node) { + if(auto it = parent.details->func.find(id); it != parent.details->func.end()) { + for(auto *curr = &it->second; curr; curr = curr->next.get()) { + if(curr->invoke == node.invoke) { + node.next = std::move(curr->next); + *curr = std::move(node); + return *curr; + } + } + + // locally overloaded function + node.next = std::make_shared(std::move(parent.details->func[id])); + } + + return parent.details->func.insert_or_assign(id, std::move(node)).first->second; +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 + void data(const id_type id, std::index_sequence) noexcept { + using data_type = std::invoke_result_t; + using args_type = type_list)>::args_type...>; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + /* this is never static */ + (std::is_member_object_pointer_v)> && ... && std::is_const_v>) ? internal::meta_traits::is_const : internal::meta_traits::is_none, + Setter::size, + &internal::resolve>>, + &meta_arg::size != 1u, type_list_element_t>...>>, + +[](meta_handle instance, meta_any value) { return (meta_setter>(*instance.operator->(), value.as_ref()) || ...); }, + &meta_getter}); + + bucket = &elem.prop; + } + +public: + /*! @brief Default constructor. */ + meta_factory() noexcept + : meta_factory{locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context into which to construct meta types. + */ + meta_factory(meta_ctx &area) noexcept + : ctx{&area}, + bucket{}, + info{&type_id()} { + auto &&elem = internal::owner(*ctx, *info); + + if(!elem.details) { + elem.details = std::make_shared(); + } + + bucket = &elem.details->prop; + } + + /** + * @brief Assigns a custom unique identifier to a meta type. + * @param id A custom unique identifier. + * @return A meta factory for the given type. + */ + auto type(const id_type id) noexcept { + auto &&elem = internal::owner(*ctx, *info); + ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier"); + bucket = &elem.details->prop; + elem.id = id; + return *this; + } + + /** + * @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() noexcept { + static_assert(!std::is_same_v && std::is_base_of_v, "Invalid base type"); + auto *const op = +[](const void *instance) noexcept { return static_cast(static_cast(static_cast(instance))); }; + internal::owner(*ctx, *info).details->base.insert_or_assign(type_id().hash(), internal::meta_base_node{&internal::resolve, op}); + bucket = nullptr; + return *this; + } + + /** + * @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() noexcept { + using conv_type = std::remove_cv_t>>; + auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast(instance))); }; + internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id().hash(), internal::meta_conv_node{op}); + bucket = nullptr; + return *this; + } + + /** + * @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() noexcept { + using conv_type = std::remove_cv_t>; + auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast(*static_cast(instance))); }; + internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id().hash(), internal::meta_conv_node{op}); + bucket = nullptr; + return *this; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * Both member functions and free function 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 not. + * + * @tparam Candidate The actual function to use as a constructor. + * @tparam Policy Optional policy (no policy set by default). + * @return A meta factory for the parent type. + */ + template + auto ctor() noexcept { + using descriptor = meta_function_helper_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + static_assert(std::is_same_v>, Type>, "The function doesn't return an object of the required type"); + internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg, &meta_construct}); + bucket = nullptr; + return *this; + } + + /** + * @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 A meta factory for the parent type. + */ + template + auto ctor() noexcept { + // default constructor is already implicitly generated, no need for redundancy + if constexpr(sizeof...(Args) != 0u) { + using descriptor = meta_function_helper_t; + internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg, &meta_construct}); + } + + bucket = nullptr; + return *this; + } + + /** + * @brief Assigns a meta destructor to a meta type. + * + * Both free functions and member functions can be assigned to meta types in + * the role of destructors.
+ * The signature of a free function should be identical to the following: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * Member functions should not take arguments instead.
+ * 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() noexcept { + static_assert(std::is_invocable_v, "The function doesn't accept an object of the type provided"); + auto *const op = +[](void *instance) { std::invoke(Func, *static_cast(instance)); }; + internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op}; + bucket = nullptr; + return *this; + } + + /** + * @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 A meta factory for the parent type. + */ + template + auto data(const id_type id) noexcept { + if constexpr(std::is_member_object_pointer_v) { + using data_type = std::invoke_result_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + /* this is never static */ + std::is_const_v> ? internal::meta_traits::is_const : internal::meta_traits::is_none, + 1u, + &internal::resolve>>, + &meta_arg>>>, + &meta_setter, + &meta_getter}); + + bucket = &elem.prop; + } else { + using data_type = std::remove_pointer_t; + + if constexpr(std::is_pointer_v) { + static_assert(Policy::template value, "Invalid return type for the given policy"); + } else { + static_assert(Policy::template value, "Invalid return type for the given policy"); + } + + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + ((std::is_same_v>> || std::is_const_v>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, + 1u, + &internal::resolve>>, + &meta_arg>>>, + &meta_setter, + &meta_getter}); + + bucket = &elem.prop; + } + + return *this; + } + + /** + * @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 A meta factory for the parent type. + */ + template + auto data(const id_type id) noexcept { + using data_type = std::invoke_result_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + if constexpr(std::is_same_v) { + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + /* this is never static */ + internal::meta_traits::is_const, + 0u, + &internal::resolve>>, + &meta_arg>, + &meta_setter, + &meta_getter}); + + bucket = &elem.prop; + } else { + using args_type = typename meta_function_helper_t::args_type; + + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + /* this is never static nor const */ + internal::meta_traits::is_none, + 1u, + &internal::resolve>>, + &meta_arg>>, + &meta_setter, + &meta_getter}); + + bucket = &elem.prop; + } + + return *this; + } + + /** + * @brief Assigns a meta data to a meta type by means of its setters and + * getter. + * + * Multi-setter support for meta data members. All setters are tried in the + * order of definition before returning to the caller.
+ * Setters can be either free functions, member functions or a mix of them + * and are provided via a `value_list` type. + * + * @sa data + * + * @tparam Setter The actual functions to use as setters. + * @tparam Getter The actual getter function. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return A meta factory for the parent type. + */ + template + auto data(const id_type id) noexcept { + data(id, std::make_index_sequence{}); + return *this; + } + + /** + * @brief Assigns a meta function 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 A meta factory for the parent type. + */ + template + auto func(const id_type id) noexcept { + using descriptor = meta_function_helper_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_func_node{ + (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none), + descriptor::args_type::size, + &internal::resolve, void, std::remove_cv_t>>>, + &meta_arg, + &meta_invoke}); + + bucket = &elem.prop; + return *this; + } + + /** + * @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 Value Optional type of the property value. + * @param id Property key. + * @param value Optional property value. + * @return A meta factory for the parent type. + */ + template + meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) { + ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties"); + + if constexpr(sizeof...(Value) == 0u) { + (*bucket)[id] = internal::meta_prop_node{&internal::resolve}; + } else { + (*bucket)[id] = internal::meta_prop_node{ + &internal::resolve>..., + std::make_shared>(std::forward(value))...}; + } + + return *this; + } + +private: + meta_ctx *ctx; + dense_map *bucket; + const type_info *info; +}; + +/** + * @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. + * @param ctx The context into which to construct meta types. + * @return A meta factory for the given type. + */ +template +[[nodiscard]] auto meta(meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + // make sure the type exists in the context before returning a factory + context.value.try_emplace(type_id().hash(), internal::resolve(context)); + return meta_factory{ctx}; +} + +/** + * @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 A meta factory for the given type. + */ +template +[[nodiscard]] auto meta() noexcept { + return meta(locator::value_or()); +} + +/** + * @brief Resets a type and all its parts. + * + * Resets a 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. + * + * The type is also removed from the set of searchable types. + * + * @param id Unique identifier. + * @param ctx The context from which to reset meta types. + */ +inline void meta_reset(meta_ctx &ctx, const id_type id) noexcept { + auto &&context = internal::meta_context::from(ctx); + + for(auto it = context.value.begin(); it != context.value.end();) { + if(it->second.id == id) { + it = context.value.erase(it); + } else { + ++it; + } + } +} + +/** + * @brief Resets a type and all its parts. + * + * Resets a 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. + * + * The type is also removed from the set of searchable types. + * + * @param id Unique identifier. + */ +inline void meta_reset(const id_type id) noexcept { + meta_reset(locator::value_or(), id); +} + +/** + * @brief Resets a type and all its parts. + * + * @sa meta_reset + * + * @tparam Type Type to reset. + * @param ctx The context from which to reset meta types. + */ +template +void meta_reset(meta_ctx &ctx) noexcept { + internal::meta_context::from(ctx).value.erase(type_id().hash()); +} + +/** + * @brief Resets a type and all its parts. + * + * @sa meta_reset + * + * @tparam Type Type to reset. + */ +template +void meta_reset() noexcept { + meta_reset(locator::value_or()); +} + +/** + * @brief Resets all meta types. + * + * @sa meta_reset + * + * @param ctx The context from which to reset meta types. + */ +inline void meta_reset(meta_ctx &ctx) noexcept { + internal::meta_context::from(ctx).value.clear(); +} + +/** + * @brief Resets all meta types. + * + * @sa meta_reset + */ +inline void meta_reset() noexcept { + meta_reset(locator::value_or()); +} + +} // namespace entt + +#endif + +// #include "meta/meta.hpp" +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "../locator/locator.hpp" + +// #include "adl_pointer.hpp" + +// #include "context.hpp" + +// #include "fwd.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; + +/*! @brief Proxy object for sequence containers. */ +class meta_sequence_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_sequence_container() noexcept + : meta_sequence_container{locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_sequence_container(const meta_ctx &area) noexcept + : ctx{&area} {} + + /** + * @brief Rebinds a proxy object to a sequence container type. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + void rebind(Type &instance) noexcept { + value_type_node = &internal::resolve; + const_reference_node = &internal::resolve>>; + size_fn = meta_sequence_container_traits>::size; + clear_fn = meta_sequence_container_traits>::clear; + reserve_fn = meta_sequence_container_traits>::reserve; + resize_fn = meta_sequence_container_traits>::resize; + begin_fn = meta_sequence_container_traits>::begin; + end_fn = meta_sequence_container_traits>::end; + insert_fn = meta_sequence_container_traits>::insert; + erase_fn = meta_sequence_container_traits>::erase; + const_only = std::is_const_v; + data = &instance; + } + + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool resize(const size_type); + inline bool clear(); + inline bool reserve(const size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline iterator insert(iterator, meta_any); + inline iterator erase(iterator); + [[nodiscard]] inline meta_any operator[](const size_type); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*const_reference_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + bool (*resize_fn)(void *, const size_type){}; + iterator (*begin_fn)(const meta_ctx &, void *, const void *){}; + iterator (*end_fn)(const meta_ctx &, void *, const void *){}; + iterator (*insert_fn)(const meta_ctx &, void *, const void *, const void *, const iterator &){}; + iterator (*erase_fn)(const meta_ctx &, void *, const iterator &){}; + const void *data{}; + bool const_only{}; +}; + +/*! @brief Proxy object for associative containers. */ +class meta_associative_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_associative_container() noexcept + : meta_associative_container{locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_associative_container(const meta_ctx &area) noexcept + : ctx{&area} {} + + /** + * @brief Rebinds a proxy object to an associative container type. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + void rebind(Type &instance) noexcept { + key_type_node = &internal::resolve; + value_type_node = &internal::resolve; + + if constexpr(!meta_associative_container_traits>::key_only) { + mapped_type_node = &internal::resolve; + } + + size_fn = &meta_associative_container_traits>::size; + clear_fn = &meta_associative_container_traits>::clear; + reserve_fn = &meta_associative_container_traits>::reserve; + begin_fn = &meta_associative_container_traits>::begin; + end_fn = &meta_associative_container_traits>::end; + insert_fn = &meta_associative_container_traits>::insert; + erase_fn = &meta_associative_container_traits>::erase; + find_fn = &meta_associative_container_traits>::find; + const_only = std::is_const_v; + data = &instance; + } + + [[nodiscard]] inline bool key_only() const noexcept; + [[nodiscard]] inline meta_type key_type() const noexcept; + [[nodiscard]] inline meta_type mapped_type() const noexcept; + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool clear(); + inline bool reserve(const size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline bool insert(meta_any, meta_any); + inline size_type erase(meta_any); + [[nodiscard]] inline iterator find(meta_any); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + internal::meta_type_node (*key_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + iterator (*begin_fn)(const meta_ctx &, void *, const void *){}; + iterator (*end_fn)(const meta_ctx &, void *, const void *){}; + bool (*insert_fn)(void *, const void *, const void *){}; + size_type (*erase_fn)(void *, const void *){}; + iterator (*find_fn)(const meta_ctx &, void *, const void *, const void *){}; + const void *data{}; + bool const_only{}; +}; + +/*! @brief Possible modes of a meta any object. */ +using meta_any_policy = any_policy; + +/*! @brief Opaque wrapper for values of any type. */ +class meta_any { + using vtable_type = void(const internal::meta_traits op, const bool, const void *, void *); + + template + static std::enable_if_t>, Type>> basic_vtable([[maybe_unused]] const internal::meta_traits req, [[maybe_unused]] const bool const_only, [[maybe_unused]] const void *value, [[maybe_unused]] void *other) { + if constexpr(is_meta_pointer_like_v) { + if(req == internal::meta_traits::is_meta_pointer_like) { + if constexpr(std::is_function_v::element_type>) { + static_cast(other)->emplace(*static_cast(value)); + } else if constexpr(!std::is_void_v::element_type>>) { + using in_place_type = decltype(adl_meta_pointer_like::dereference(*static_cast(value))); + + if constexpr(std::is_constructible_v) { + if(const auto &pointer_like = *static_cast(value); pointer_like) { + static_cast(other)->emplace(adl_meta_pointer_like::dereference(pointer_like)); + } + } else { + static_cast(other)->emplace(adl_meta_pointer_like::dereference(*static_cast(value))); + } + } + } + } + + if constexpr(is_complete_v>) { + if(req == internal::meta_traits::is_meta_sequence_container) { + const_only ? static_cast(other)->rebind(*static_cast(value)) : static_cast(other)->rebind(*static_cast(const_cast(value))); + } + } + + if constexpr(is_complete_v>) { + if(req == internal::meta_traits::is_meta_associative_container) { + const_only ? static_cast(other)->rebind(*static_cast(value)) : static_cast(other)->rebind(*static_cast(const_cast(value))); + } + } + } + + void release() { + if(node.dtor.dtor && (storage.policy() == any_policy::owner)) { + node.dtor.dtor(storage.data()); + } + } + + meta_any(const meta_ctx &area, const meta_any &other, any ref) noexcept + : storage{std::move(ref)}, + ctx{&area}, + node{storage ? other.node : internal::meta_type_node{}}, + vtable{storage ? other.vtable : &basic_vtable} {} + +public: + /*! Default constructor. */ + meta_any() noexcept + : meta_any{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_any(meta_ctx_arg_t, const meta_ctx &area) noexcept + : storage{}, + ctx{&area}, + node{}, + vtable{&basic_vtable} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @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, Args &&...args) + : meta_any{locator::value_or(), std::in_place_type, std::forward(args)...} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param area The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(const meta_ctx &area, std::in_place_type_t, Args &&...args) + : storage{std::in_place_type, std::forward(args)...}, + ctx{&area}, + node{internal::resolve>>(internal::meta_context::from(*ctx))}, + vtable{&basic_vtable>>} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(Type &&value) + : meta_any{locator::value_or(), std::forward(value)} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param area The context from which to search for meta types. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(const meta_ctx &area, Type &&value) + : meta_any{area, std::in_place_type>, std::forward(value)} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_any(const meta_ctx &area, const meta_any &other) + : meta_any{other} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_any(const meta_ctx &area, meta_any &&other) + : meta_any{std::move(other)} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + meta_any(const meta_any &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + meta_any(meta_any &&other) noexcept + : storage{std::move(other.storage)}, + ctx{other.ctx}, + node{std::exchange(other.node, internal::meta_type_node{})}, + vtable{std::exchange(other.vtable, &basic_vtable)} {} + + /*! @brief Frees the internal storage, whatever it means. */ + ~meta_any() { + release(); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This meta any object. + */ + meta_any &operator=(const meta_any &other) { + release(); + storage = other.storage; + ctx = other.ctx; + node = other.node; + vtable = other.vtable; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ + meta_any &operator=(meta_any &&other) noexcept { + release(); + storage = std::move(other.storage); + ctx = other.ctx; + node = std::exchange(other.node, internal::meta_type_node{}); + vtable = std::exchange(other.vtable, &basic_vtable); + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This meta any object. + */ + template + std::enable_if_t, meta_any>, meta_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /*! @copydoc any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /*! @copydoc any::data */ + [[nodiscard]] const void *data() const noexcept { + return storage.data(); + } + + /*! @copydoc any::data */ + [[nodiscard]] void *data() noexcept { + return storage.data(); + } + + /** + * @brief Invokes the underlying function, if possible. + * @tparam Args Types of arguments to use to invoke the function. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, Args &&...args) const; + + /*! @copydoc invoke */ + template + meta_any invoke(const id_type id, Args &&...args); + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, Type &&value); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id) const; + + /*! @copydoc get */ + [[nodiscard]] meta_any get(const id_type id); + + /** + * @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 + [[nodiscard]] const Type *try_cast() const { + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data())); + } + + /*! @copydoc try_cast */ + template + [[nodiscard]] Type *try_cast() { + if constexpr(std::is_const_v) { + return std::as_const(*this).try_cast>(); + } else { + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(const_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data()))); + } + } + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A reference to the contained instance. + */ + template + [[nodiscard]] Type cast() const { + auto *const instance = try_cast>(); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); + } + + /*! @copydoc cast */ + template + [[nodiscard]] Type cast() { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = try_cast>(); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ + [[nodiscard]] meta_any allow_cast(const meta_type &type) const; + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ + [[nodiscard]] bool allow_cast(const meta_type &type) { + if(auto other = std::as_const(*this).allow_cast(type); other) { + if((other.storage.policy() == any_policy::owner)) { + std::swap(*this, other); + } + + return true; + } + + return false; + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ + template + [[nodiscard]] meta_any allow_cast() const { + if constexpr(std::is_reference_v && !std::is_const_v>) { + return meta_any{meta_ctx_arg, *ctx}; + } else { + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}); + } + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ + template + [[nodiscard]] bool allow_cast() { + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}) && (!(std::is_reference_v && !std::is_const_v>) || storage.data() != nullptr); + } + + /*! @copydoc any::emplace */ + template + void emplace(Args &&...args) { + release(); + storage.emplace(std::forward(args)...); + node = internal::resolve>>(internal::meta_context::from(*ctx)); + vtable = &basic_vtable>>; + } + + /*! @copydoc any::assign */ + bool assign(const meta_any &other); + + /*! @copydoc any::assign */ + bool assign(meta_any &&other); + + /*! @copydoc any::reset */ + void reset() { + release(); + storage.reset(); + node = {}; + vtable = &basic_vtable; + } + + /** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ + [[nodiscard]] meta_sequence_container as_sequence_container() noexcept { + meta_sequence_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_sequence_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy); + return proxy; + } + + /*! @copydoc as_sequence_container */ + [[nodiscard]] meta_sequence_container as_sequence_container() const noexcept { + meta_sequence_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_sequence_container, true, data(), &proxy); + return proxy; + } + + /** + * @brief Returns an associative container proxy. + * @return An associative container proxy for the underlying object. + */ + [[nodiscard]] meta_associative_container as_associative_container() noexcept { + meta_associative_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_associative_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy); + return proxy; + } + + /*! @copydoc as_associative_container */ + [[nodiscard]] meta_associative_container as_associative_container() const noexcept { + meta_associative_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_associative_container, true, data(), &proxy); + return proxy; + } + + /** + * @brief Indirection operator for dereferencing opaque objects. + * @return A wrapper that shares a reference to an unmanaged object if the + * wrapped element is dereferenceable, an invalid meta any otherwise. + */ + [[nodiscard]] meta_any operator*() const noexcept { + meta_any ret{meta_ctx_arg, *ctx}; + vtable(internal::meta_traits::is_meta_pointer_like, true, storage.data(), &ret); + return ret; + } + + /** + * @brief Returns false if a wrapper is invalid, true otherwise. + * @return False if the wrapper is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(node.info == nullptr); + } + + /*! @copydoc any::operator== */ + [[nodiscard]] bool operator==(const meta_any &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info && storage == other.storage)); + } + + /*! @copydoc any::operator!= */ + [[nodiscard]] bool operator!=(const meta_any &other) const noexcept { + return !(*this == other); + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() const noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; + } + + /*! @copydoc any::owner */ + [[deprecated("use policy() and meta_any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (storage.policy() == any_policy::owner); + } + + /** + * @brief Returns the current mode of a meta any object. + * @return The current mode of the meta any object. + */ + [[nodiscard]] meta_any_policy policy() const noexcept { + return storage.policy(); + } + +private: + any storage; + const meta_ctx *ctx; + internal::meta_type_node node; + vtable_type *vtable; +}; + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @param ctx The context from which to search for meta types. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { + return meta_any{ctx, std::in_place_type, std::forward(value)}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(Type &&value) { + return forward_as_meta(locator::value_or(), std::forward(value)); +} + +/** + * @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. + */ +struct meta_handle { + /*! Default constructor. */ + meta_handle() noexcept + : meta_handle{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_handle(meta_ctx_arg_t, const meta_ctx &area) noexcept + : any{meta_ctx_arg, area} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(const meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param ctx The context from which to search for meta types. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(const meta_ctx &ctx, Type &value) noexcept + : any{ctx, std::in_place_type, value} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(Type &value) noexcept + : meta_handle{locator::value_or(), value} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_handle(const meta_ctx &area, const meta_handle &other) + : any{area, other.any} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_handle(const meta_ctx &area, meta_handle &&other) + : any{area, std::move(other.any)} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + meta_handle(const meta_handle &) = delete; + + /*! @brief Default move constructor. */ + meta_handle(meta_handle &&) = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This meta handle. + */ + meta_handle &operator=(const meta_handle &) = delete; + + /** + * @brief Default move assignment operator. + * @return This meta handle. + */ + meta_handle &operator=(meta_handle &&) = default; + + /** + * @brief Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(any); + } + + /*! @copydoc meta_any::operator== */ + [[nodiscard]] bool operator==(const meta_handle &other) const noexcept { + return (any == other.any); + } + + /*! @copydoc meta_any::operator!= */ + [[nodiscard]] bool operator!=(const meta_handle &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Access operator for accessing the contained opaque object. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any *operator->() { + return &any; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const meta_any *operator->() const { + return &any; + } + +private: + meta_any any; +}; + +/*! @brief Opaque wrapper for properties of any type. */ +struct meta_prop { + /*! @brief Default constructor. */ + meta_prop() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_prop(const meta_ctx &area, const internal::meta_prop_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the stored value by const reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() const { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns the stored value by reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, node->value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /** + * @brief Checks if two objects refer to the same type. + * @param other The object with which to compare. + * @return True if the objects refer to the same type, false otherwise. + */ + [[nodiscard]] bool operator==(const meta_prop &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_prop_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_prop &lhs, const meta_prop &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for data members. */ +struct meta_data { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_data_node::size_type; + + /*! @brief Default constructor. */ + meta_data() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the number of setters available. + * @return The number of setters available. + */ + [[nodiscard]] size_type arity() const noexcept { + return node->arity; + } + + /** + * @brief Indicates whether a data member is constant or not. + * @return True if the data member is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a data member is static or not. + * @return True if the data member is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); + } + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /** + * @brief Sets the value of a given variable. + * @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 && node->set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward(value)}); + } + + /** + * @brief Gets the value of a given variable. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(meta_handle instance) const { + return node->get(*ctx, meta_handle{*ctx, std::move(instance)}); + } + + /** + * @brief Returns the type accepted by the i-th setter. + * @param index Index of the setter of which to return the accepted type. + * @return The type accepted by the i-th setter. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; + + /** + * @brief Returns a range to visit registered meta properties. + * @return An iterable range to visit registered meta properties. + */ + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; + } + + /** + * @brief Lookup utility for meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_data &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_data_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for member functions. */ +struct meta_func { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_func_node::size_type; + + /*! @brief Default constructor. */ + meta_func() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the number of arguments accepted by a member function. + * @return The number of arguments accepted by the member function. + */ + [[nodiscard]] size_type arity() const noexcept { + return node->arity; + } + + /** + * @brief Indicates whether a member function is constant or not. + * @return True if the member function is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a member function is static or not. + * @return True if the member function is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); + } + + /** + * @brief Returns the return type of a member function. + * @return The return type of the member function. + */ + [[nodiscard]] inline meta_type ret() const noexcept; + + /** + * @brief Returns the type of the i-th argument of a member function. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a member function. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; + + /** + * @brief Invokes the underlying function, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { + return sz == arity() ? node->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @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 wrapper containing the returned value, if any. + */ + template + meta_any invoke(meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(std::move(instance), arguments, sizeof...(Args)); + } + + /*! @copydoc meta_data::prop */ + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; + } + + /** + * @brief Lookup utility for meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } + + /** + * @brief Returns the next overload of a given function, if any. + * @return The next overload of the given function, if any. + */ + [[nodiscard]] meta_func next() const { + return node->next ? meta_func{*ctx, *node->next} : meta_func{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_func &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_func_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for types. */ +class meta_type { + template + [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const { + decltype(next()) candidate = nullptr; + size_type same{}; + bool ambiguous{}; + + for(auto curr = next(); curr; curr = next()) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(constness && !static_cast(curr->traits & internal::meta_traits::is_const)) { + continue; + } + } + + if(curr->arity == sz) { + size_type match{}; + size_type pos{}; + + for(; pos < sz && args[pos]; ++pos) { + const auto other = curr->arg(*ctx, pos); + const auto type = args[pos].type(); + + if(const auto &info = other.info(); info == type.info()) { + ++match; + } else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash()))) || (type.node.conversion_helper && other.node.conversion_helper))) { + break; + } + } + + if(pos == sz) { + if(!candidate || match > same) { + candidate = curr; + same = match; + ambiguous = false; + } else if(match == same) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(static_cast(curr->traits & internal::meta_traits::is_const) != static_cast(candidate->traits & internal::meta_traits::is_const)) { + candidate = static_cast(candidate->traits & internal::meta_traits::is_const) ? curr : candidate; + ambiguous = false; + continue; + } + } + + ambiguous = true; + } + } + } + } + + return ambiguous ? nullptr : candidate; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_type_node::size_type; + + /*! @brief Default constructor. */ + meta_type() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept + : node{curr}, + ctx{&area} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept + : meta_type{area, curr.type(internal::meta_context::from(area))} {} + + /** + * @brief Returns the type info object of the underlying type. + * @return The type info object of the underlying type. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *node.info; + } + + /** + * @brief Returns the identifier assigned to a type. + * @return The identifier assigned to the type. + */ + [[nodiscard]] id_type id() const noexcept { + return node.id; + } + + /** + * @brief Returns the size of the underlying type if known. + * @return The size of the underlying type if known, 0 otherwise. + */ + [[nodiscard]] size_type size_of() const noexcept { + return node.size_of; + } + + /** + * @brief Checks whether a type refers to an arithmetic type or not. + * @return True if the underlying type is an arithmetic type, false + * otherwise. + */ + [[nodiscard]] bool is_arithmetic() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_arithmetic); + } + + /** + * @brief Checks whether a type refers to an integral type or not. + * @return True if the underlying type is an integral type, false otherwise. + */ + [[nodiscard]] bool is_integral() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_integral); + } + + /** + * @brief Checks whether a type refers to a signed type or not. + * @return True if the underlying type is a signed type, false otherwise. + */ + [[nodiscard]] bool is_signed() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_signed); + } + + /** + * @brief Checks whether a type refers to an array type or not. + * @return True if the underlying type is an array type, false otherwise. + */ + [[nodiscard]] bool is_array() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_array); + } + + /** + * @brief Checks whether a type refers to an enum or not. + * @return True if the underlying type is an enum, false otherwise. + */ + [[nodiscard]] bool is_enum() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_enum); + } + + /** + * @brief Checks whether a type refers to a class or not. + * @return True if the underlying type is a class, false otherwise. + */ + [[nodiscard]] bool is_class() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_class); + } + + /** + * @brief Checks whether a type refers to a pointer or not. + * @return True if the underlying type is a pointer, false otherwise. + */ + [[nodiscard]] bool is_pointer() const noexcept { + return node.info && (node.info->hash() != remove_pointer().info().hash()); + } + + /** + * @brief Provides the type for which the pointer is defined. + * @return The type for which the pointer is defined or this type if it + * doesn't refer to a pointer type. + */ + [[nodiscard]] meta_type remove_pointer() const noexcept { + return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; // NOLINT + } + + /** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is pointer-like, false otherwise. + */ + [[nodiscard]] bool is_pointer_like() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_pointer_like); + } + + /** + * @brief Checks whether a type refers to a sequence container or not. + * @return True if the type is a sequence container, false otherwise. + */ + [[nodiscard]] bool is_sequence_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_sequence_container); + } + + /** + * @brief Checks whether a type refers to an associative container or not. + * @return True if the type is an associative container, false otherwise. + */ + [[nodiscard]] bool is_associative_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_associative_container); + } + + /** + * @brief Checks whether a type refers to a recognized class template + * specialization or not. + * @return True if the type is a recognized class template specialization, + * false otherwise. + */ + [[nodiscard]] bool is_template_specialization() const noexcept { + return (node.templ.arity != 0u); + } + + /** + * @brief Returns the number of template arguments. + * @return The number of template arguments. + */ + [[nodiscard]] size_type template_arity() const noexcept { + return node.templ.arity; + } + + /** + * @brief Returns a tag for the class template of the underlying type. + * @return The tag for the class template of the underlying type. + */ + [[nodiscard]] inline meta_type template_type() const noexcept { + return node.templ.type ? meta_type{*ctx, node.templ.type(internal::meta_context::from(*ctx))} : meta_type{}; + } + + /** + * @brief Returns the type of the i-th template argument of a type. + * @param index Index of the template argument of which to return the type. + * @return The type of the i-th template argument of a type. + */ + [[nodiscard]] inline meta_type template_arg(const size_type index) const noexcept { + return index < template_arity() ? meta_type{*ctx, node.templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{}; + } + + /** + * @brief Checks if a type supports direct casting to another type. + * @param other The meta type to test for. + * @return True if direct casting is allowed, false otherwise. + */ + [[nodiscard]] bool can_cast(const meta_type &other) const noexcept { + // casting this is UB in all cases but we aren't going to use the resulting pointer, so... + return (internal::try_cast(internal::meta_context::from(*ctx), node, other.node, this) != nullptr); + } + + /** + * @brief Checks if a type supports conversion it to another type. + * @param other The meta type to test for. + * @return True if the conversion is allowed, false otherwise. + */ + [[nodiscard]] bool can_convert(const meta_type &other) const noexcept { + return (internal::try_convert(internal::meta_context::from(*ctx), node, other.info(), other.is_arithmetic() || other.is_enum(), nullptr, [](const void *, auto &&...args) { return ((static_cast(args), 1) + ... + 0u); }) != 0u); + } + + /** + * @brief Returns a range to visit registered top-level base meta types. + * @return An iterable range to visit registered top-level base meta types. + */ + [[nodiscard]] meta_range base() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->base.cbegin()}, {*ctx, node.details->base.cend()}} : range_type{}; + } + + /** + * @brief Returns a range to visit registered top-level meta data. + * @return An iterable range to visit registered top-level meta data. + */ + [[nodiscard]] meta_range data() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->data.cbegin()}, {*ctx, node.details->data.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta data (bases are also visited). + * @param id Unique identifier. + * @return The registered meta data for the given identifier, if any. + */ + [[nodiscard]] meta_data data(const id_type id) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), node, id); + return elem ? meta_data{*ctx, *elem} : meta_data{}; + } + + /** + * @brief Returns a range to visit registered top-level functions. + * @return An iterable range to visit registered top-level functions. + */ + [[nodiscard]] meta_range func() const noexcept { + using return_type = meta_range; + return node.details ? return_type{{*ctx, node.details->func.cbegin()}, {*ctx, node.details->func.cend()}} : return_type{}; + } + + /** + * @brief Lookup utility for meta functions (bases are also visited). + * + * In case of overloaded functions, a random one is returned. + * + * @param id Unique identifier. + * @return The registered meta function for the given identifier, if any. + */ + [[nodiscard]] meta_func func(const id_type id) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), node, id); + return elem ? meta_func{*ctx, *elem} : meta_func{}; + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { + if(node.details) { + if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &(first++)->second; }); candidate) { + return candidate->invoke(*ctx, args); + } + } + + if(sz == 0u && node.default_constructor) { + return node.default_constructor(*ctx); + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief construct + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + template + [[nodiscard]] meta_any construct(Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return construct(arguments, sizeof...(Args)); + } + + /** + * @brief Wraps an opaque element of the underlying type. + * @param element A valid pointer to an element of the underlying type. + * @return A wrapper that references the given instance. + */ + [[nodiscard]] meta_any from_void(void *element) const { + return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /*! @copydoc from_void */ + [[nodiscard]] meta_any from_void(const void *element) const { + return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Invokes a function given an identifier, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { + if(node.details) { + if(auto it = node.details->func.find(id); it != node.details->func.cend()) { + if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { + return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); + } + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.invoke(id, *instance.operator->(), args, sz); elem) { + return elem; + } + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @param id Unique identifier. + * @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 wrapper containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(id, std::move(instance), arguments, sizeof...(Args)); + } + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @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(const id_type id, meta_handle instance, Type &&value) const { + const auto candidate = data(id); + return candidate && candidate.set(std::move(instance), std::forward(value)); + } + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { + const auto candidate = data(id); + return candidate ? candidate.get(std::move(instance)) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns a range to visit registered top-level meta properties. + * @return An iterable range to visit registered top-level meta properties. + */ + [[nodiscard]] meta_range prop() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->prop.cbegin()}, {*ctx, node.details->prop.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta properties (bases are also visited). + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::prop>(internal::meta_context::from(*ctx), node, key); + return elem ? meta_prop{*ctx, *elem} : meta_prop{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(ctx == nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_type &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info)); + } + +private: + internal::meta_type_node node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept { + return !(lhs == rhs); +} + +[[nodiscard]] inline meta_type meta_any::type() const noexcept { + return node.info ? meta_type{*ctx, node} : meta_type{}; +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) const { + return type().invoke(id, *this, std::forward(args)...); +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) { + return type().invoke(id, *this, std::forward(args)...); +} + +template +bool meta_any::set(const id_type id, Type &&value) { + return type().set(id, *this, std::forward(value)); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) const { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { + return internal::try_convert(internal::meta_context::from(*ctx), node, type.info(), type.is_arithmetic() || type.is_enum(), data(), [this, &type]([[maybe_unused]] const void *instance, auto &&...args) { + if constexpr((std::is_same_v>, internal::meta_type_node> || ...)) { + return (args.from_void(*ctx, nullptr, instance), ...); + } else if constexpr((std::is_same_v>, internal::meta_conv_node> || ...)) { + return (args.conv(*ctx, instance), ...); + } else if constexpr((std::is_same_v>, decltype(internal::meta_type_node::conversion_helper)> || ...)) { + // exploits the fact that arithmetic types and enums are also default constructible + auto other = type.construct(); + const auto value = (args(nullptr, instance), ...); + other.node.conversion_helper(other.data(), &value); + return other; + } else { + // forwards to force a compile-time error in case of available arguments + return meta_any{meta_ctx_arg, *ctx, std::forward(args)...}; + } + }); +} + +inline bool meta_any::assign(const meta_any &other) { + auto value = other.allow_cast({*ctx, node}); + return value && storage.assign(value.storage); +} + +inline bool meta_any::assign(meta_any &&other) { + if(*node.info == *other.node.info) { + return storage.assign(std::move(other.storage)); + } + + return assign(std::as_const(other)); +} + +[[nodiscard]] inline meta_type meta_data::type() const noexcept { + return meta_type{*ctx, node->type(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_func::ret() const noexcept { + return meta_type{*ctx, node->ret(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +class meta_sequence_container::meta_iterator final { + using vtable_type = void(const void *, const std::ptrdiff_t, meta_any *); + + template + static void basic_vtable(const void *value, const std::ptrdiff_t offset, meta_any *other) { + const auto &it = *static_cast(value); + other ? other->emplace(*it) : std::advance(const_cast(it), offset); + } + +public: + using difference_type = std::ptrdiff_t; + using value_type = meta_any; + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::bidirectional_iterator_tag; + + meta_iterator() noexcept + : meta_iterator{locator::value_or()} {} + + meta_iterator(const meta_ctx &area) noexcept + : ctx{&area} {} + + template + meta_iterator(const meta_ctx &area, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + vtable(handle.data(), 1, nullptr); + return *this; + } + + meta_iterator operator++(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), ++value, nullptr); + return orig; + } + + meta_iterator &operator--() noexcept { + vtable(handle.data(), -1, nullptr); + return *this; + } + + meta_iterator operator--(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), --value, nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{meta_ctx_arg, *ctx}; + vtable(handle.data(), 0, &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { + return !(*this == other); + } + + [[nodiscard]] const any &base() const noexcept { + return handle; + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; + +class meta_associative_container::meta_iterator final { + using vtable_type = void(const void *, std::pair *); + + template + static void basic_vtable(const void *value, std::pair *other) { + if(const auto &it = *static_cast(value); other) { + if constexpr(KeyOnly) { + other->first.emplace(*it); + } else { + other->first.emplacefirst))>(it->first); + other->second.emplacesecond))>(it->second); + } + } else { + ++const_cast(it); + } + } + +public: + using difference_type = std::ptrdiff_t; + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + meta_iterator() noexcept + : meta_iterator{locator::value_or()} {} + + meta_iterator(const meta_ctx &area) noexcept + : ctx{&area} {} + + template + meta_iterator(const meta_ctx &area, std::bool_constant, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + vtable(handle.data(), nullptr); + return *this; + } + + meta_iterator operator++(int) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}}; + vtable(handle.data(), &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { + return !(*this == other); + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; +/*! @endcond */ + +/** + * @brief Returns the meta value type of a container. + * @return The meta value type of the container. + */ +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @brief Returns the size of a container. + * @return The size of the container. + */ +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const noexcept { + return size_fn(data); +} + +/** + * @brief Resizes a container to contain a given number of elements. + * @param sz The new size of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::resize(const size_type sz) { + return !const_only && resize_fn(const_cast(data), sz); +} + +/** + * @brief Clears the content of a container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/** + * @brief Reserves storage for at least the given number of elements. + * @param sz The new capacity of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/** + * @brief Returns an iterator to the first element of a container. + * @return An iterator to the first element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { + return begin_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @brief Returns an iterator that is past the last element of a container. + * @return An iterator that is past the last element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { + return end_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @brief Inserts an element at a specified location of a container. + * @param it Iterator before which the element will be inserted. + * @param value Element value to insert. + * @return A possibly invalid iterator to the inserted element. + */ +inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) { + // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector + if(const auto vtype = value_type_node(internal::meta_context::from(*ctx)); !const_only && (value.allow_cast({*ctx, vtype}) || value.allow_cast({*ctx, const_reference_node(internal::meta_context::from(*ctx))}))) { + const bool is_value_type = (value.type().info() == *vtype.info); + return insert_fn(*ctx, const_cast(data), is_value_type ? std::as_const(value).data() : nullptr, is_value_type ? nullptr : std::as_const(value).data(), it); + } + + return iterator{*ctx}; +} + +/** + * @brief Removes a given element from a container. + * @param it Iterator to the element to remove. + * @return A possibly invalid iterator following the last removed element. + */ +inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) { + return const_only ? iterator{*ctx} : erase_fn(*ctx, const_cast(data), it); +} + +/** + * @brief Returns a reference to the element at a given location of a container + * (no bounds checking is performed). + * @param pos The position of the element to return. + * @return A reference to the requested element properly wrapped. + */ +[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) { + auto it = begin(); + it.operator++(static_cast(pos) - 1); + return *it; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_sequence_container::operator bool() const noexcept { + return (data != nullptr); +} + +/** + * @brief Returns true if a container is also key-only, false otherwise. + * @return True if the associative container is also key-only, false otherwise. + */ +[[deprecated("use mapped_type() instead")]] [[nodiscard]] inline bool meta_associative_container::key_only() const noexcept { + return (mapped_type_node == nullptr); +} + +/** + * @brief Returns the meta key type of a container. + * @return The meta key type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept { + return key_type_node ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @brief Returns the meta mapped type of a container. + * @return The meta mapped type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept { + return mapped_type_node ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept { + return size_fn(data); +} + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/*! @copydoc meta_sequence_container::reserve */ +inline bool meta_associative_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { + return begin_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { + return end_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @brief Inserts a key-only or key/value element into a container. + * @param key The key of the element to insert. + * @param value The value of the element to insert, if needed. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { + return !const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) + && (!mapped_type_node || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))})) + && insert_fn(const_cast(data), std::as_const(key).data(), std::as_const(value).data()); +} + +/** + * @brief Removes the specified element from a container. + * @param key The key of the element to remove. + * @return A bool denoting whether the removal took place. + */ +inline meta_associative_container::size_type meta_associative_container::erase(meta_any key) { + return (!const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})) ? erase_fn(const_cast(data), std::as_const(key).data()) : 0u; +} + +/** + * @brief Returns an iterator to the element with a given key, if any. + * @param key The key of the element to search. + * @return An iterator to the element with the given key, if any. + */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { + return key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast(data), data, std::as_const(key).data()) : iterator{*ctx}; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_associative_container::operator bool() const noexcept { + return (data != nullptr); +} + +} // namespace entt + +#endif + +// #include "meta/node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #include "../core/attribute.h" + +// #include "../core/enum.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "context.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; +struct meta_handle; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class meta_traits : std::uint32_t { + is_none = 0x0000, + is_const = 0x0001, + is_static = 0x0002, + is_arithmetic = 0x0004, + is_integral = 0x0008, + is_signed = 0x0010, + is_array = 0x0020, + is_enum = 0x0040, + is_class = 0x0080, + is_meta_pointer_like = 0x0100, + is_meta_sequence_container = 0x0200, + is_meta_associative_container = 0x0400, + _entt_enum_as_bitmask +}; + +struct meta_type_node; + +struct meta_prop_node { + meta_type_node (*type)(const meta_context &) noexcept {}; + std::shared_ptr value{}; +}; + +struct meta_base_node { + meta_type_node (*type)(const meta_context &) noexcept {}; + const void *(*cast)(const void *) noexcept {}; +}; + +struct meta_conv_node { + meta_any (*conv)(const meta_ctx &, const void *){}; +}; + +struct meta_ctor_node { + using size_type = std::size_t; + + size_type arity{0u}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_any *const){}; +}; + +struct meta_dtor_node { + void (*dtor)(void *){}; +}; + +struct meta_data_node { + using size_type = std::size_t; + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + bool (*set)(meta_handle, meta_any){}; + meta_any (*get)(const meta_ctx &, meta_handle){}; + dense_map prop{}; +}; + +struct meta_func_node { + using size_type = std::size_t; + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*ret)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){}; + std::shared_ptr next{}; + dense_map prop{}; +}; + +struct meta_template_node { + using size_type = std::size_t; + + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type_node (*arg)(const meta_context &, const size_type) noexcept {}; +}; + +struct meta_type_descriptor { + dense_map ctor{}; + dense_map base{}; + dense_map conv{}; + dense_map data{}; + dense_map func{}; + dense_map prop{}; +}; + +struct meta_type_node { + using size_type = std::size_t; + + const type_info *info{}; + id_type id{}; + meta_traits traits{meta_traits::is_none}; + size_type size_of{0u}; + meta_type_node (*resolve)(const meta_context &) noexcept {}; + meta_type_node (*remove_pointer)(const meta_context &) noexcept {}; + meta_any (*default_constructor)(const meta_ctx &){}; + double (*conversion_helper)(void *, const void *){}; + meta_any (*from_void)(const meta_ctx &, void *, const void *){}; + meta_template_node templ{}; + meta_dtor_node dtor{}; + std::shared_ptr details{}; +}; + +template +auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) { + if(node.details) { + if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) { + return &it->second; + } + + for(auto &&curr: node.details->base) { + if(auto *elem = look_for(context, curr.second.type(context), id); elem) { + return elem; + } + } + } + + return static_cast*Member)>::mapped_type *>(nullptr); +} + +template +meta_type_node resolve(const meta_context &) noexcept; + +template +[[nodiscard]] auto meta_arg_node(const meta_context &context, type_list, [[maybe_unused]] const std::size_t index) noexcept { + [[maybe_unused]] std::size_t pos{}; + meta_type_node (*value)(const meta_context &) noexcept = nullptr; + ((value = (pos++ == index ? &resolve>> : value)), ...); + ENTT_ASSERT(value != nullptr, "Out of bounds"); + return value(context); +} + +[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept { + if(from.info && to.info && *from.info == *to.info) { + return instance; + } + + if(from.details) { + for(auto &&curr: from.details->base) { + if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) { + return elem; + } + } + } + + return nullptr; +} + +template +[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) { + if(from.info && *from.info == to) { + return func(instance, from); + } + + if(from.details) { + if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) { + return func(instance, it->second); + } + + for(auto &&curr: from.details->base) { + if(auto other = try_convert(context, curr.second.type(context), to, arithmetic_or_enum, curr.second.cast(instance), func); other) { + return other; + } + } + } + + if(from.conversion_helper && arithmetic_or_enum) { + return func(instance, from.conversion_helper); + } + + return func(instance); +} + +[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept { + const auto it = context.value.find(info.hash()); + return it != context.value.end() ? &it->second : nullptr; +} + +template +[[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept { + static_assert(std::is_same_v>>, "Invalid type"); + + if(auto *elem = try_resolve(context, type_id()); elem) { + return *elem; + } + + meta_type_node node{ + &type_id(), + type_id().hash(), + (std::is_arithmetic_v ? meta_traits::is_arithmetic : meta_traits::is_none) + | (std::is_integral_v ? meta_traits::is_integral : meta_traits::is_none) + | (std::is_signed_v ? meta_traits::is_signed : meta_traits::is_none) + | (std::is_array_v ? meta_traits::is_array : meta_traits::is_none) + | (std::is_enum_v ? meta_traits::is_enum : meta_traits::is_none) + | (std::is_class_v ? meta_traits::is_class : meta_traits::is_none) + | (is_meta_pointer_like_v ? meta_traits::is_meta_pointer_like : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_sequence_container : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_associative_container : meta_traits::is_none), + size_of_v, + &resolve, + &resolve>>}; + + if constexpr(std::is_default_constructible_v) { + node.default_constructor = +[](const meta_ctx &ctx) { + return meta_any{ctx, std::in_place_type}; + }; + } + + if constexpr(std::is_arithmetic_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(*static_cast(value))) : static_cast(*static_cast(value)); + }; + } else if constexpr(std::is_enum_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); + }; + } + + if constexpr(!std::is_void_v && !std::is_function_v) { + node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) { + if(element) { + return meta_any{ctx, std::in_place_type &>, *static_cast *>(element)}; + } + + return meta_any{ctx, std::in_place_type &>, *static_cast *>(as_const)}; + }; + } + + if constexpr(is_complete_v>) { + node.templ = meta_template_node{ + meta_template_traits::args_type::size, + &resolve::class_type>, + +[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits::args_type{}, index); }}; + } + + return node; +} + +} // namespace internal +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "meta/pointer.hpp" +#ifndef ENTT_META_POINTER_HPP +#define ENTT_META_POINTER_HPP + +#include +#include +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @brief Makes plain pointers pointer-like types for the meta system. + * @tparam Type Element type. + */ +template +struct is_meta_pointer_like + : std::true_type {}; + +/** + * @brief Partial specialization used to reject pointers to arrays. + * @tparam Type Type of elements of the array. + * @tparam N Number of elements of the array. + */ +template +struct is_meta_pointer_like + : std::false_type {}; + +/** + * @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + */ +template +struct is_meta_pointer_like> + : std::true_type {}; + +/** + * @brief Makes `std::unique_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + * @tparam Args Other arguments. + */ +template +struct is_meta_pointer_like> + : std::true_type {}; + +} // namespace entt + +#endif + +// #include "meta/policy.hpp" +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + +#include + +namespace entt { + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v && !std::is_const_v>; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_is_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/** + * @brief Provides the member constant `value` to true if a type also is a meta + * policy, false otherwise. + * @tparam Type Type to check. + */ +template +struct is_meta_policy + : std::bool_constant || std::is_same_v || std::is_same_v || std::is_same_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type Type to check. + */ +template +inline constexpr bool is_meta_policy_v = is_meta_policy::value; + +} // namespace entt + +#endif + +// #include "meta/range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "context.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct meta_range_iterator final { + using difference_type = std::ptrdiff_t; + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr meta_range_iterator() noexcept + : it{}, + ctx{} {} + + constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept + : it{iter}, + ctx{&area} {} + + constexpr meta_range_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr meta_range_iterator operator++(int) noexcept { + meta_range_iterator orig = *this; + return ++(*this), orig; + } + + constexpr meta_range_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr meta_range_iterator operator--(int) noexcept { + meta_range_iterator orig = *this; + return operator--(), orig; + } + + constexpr meta_range_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr meta_range_iterator operator+(const difference_type value) const noexcept { + meta_range_iterator copy = *this; + return (copy += value); + } + + constexpr meta_range_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr meta_range_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, Type{*ctx, it[value].second}}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->first, Type{*ctx, it->second}}; + } + + template + friend constexpr std::ptrdiff_t operator-(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator==(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator<(const meta_range_iterator &, const meta_range_iterator &) noexcept; + +private: + It it; + const meta_ctx *ctx; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects returned. + * @tparam It Type of forward iterator. + */ +template +using meta_range = iterable_adaptor>; + +} // namespace entt + +#endif + +// #include "meta/resolve.hpp" +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + +#include +// #include "../core/type_info.hpp" + +// #include "../locator/locator.hpp" + +// #include "context.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + + +namespace entt { + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @param ctx The context from which to search for meta types. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::resolve>>(context)}; +} + +/** + * @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 +[[nodiscard]] meta_type resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns a range to use to visit all meta types. + * @param ctx The context from which to search for meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}}; +} + +/** + * @brief Returns a range to use to visit all meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param ctx The context from which to search for meta types. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept { + for(auto &&curr: resolve(ctx)) { + if(curr.second.id() == id) { + return curr.second; + } + } + + return meta_type{}; +} + +/** + * @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. + */ +[[nodiscard]] inline meta_type resolve(const id_type id) noexcept { + return resolve(locator::value_or(), id); +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param ctx The context from which to search for meta types. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept { + auto &&context = internal::meta_context::from(ctx); + const auto *elem = internal::try_resolve(context, info); + return elem ? meta_type{ctx, *elem} : meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const type_info &info) noexcept { + return resolve(locator::value_or(), info); +} + +} // namespace entt + +#endif + +// #include "meta/template.hpp" +#ifndef ENTT_META_TEMPLATE_HPP +#define ENTT_META_TEMPLATE_HPP + +// #include "../core/type_traits.hpp" + + +namespace entt { + +/*! @brief Utility class to disambiguate class templates. */ +template class> +struct meta_class_template_tag {}; + +/** + * @brief General purpose traits class for generating meta template information. + * @tparam Clazz Type of class template. + * @tparam Args Types of template arguments. + */ +template class Clazz, typename... Args> +struct meta_template_traits> { + /*! @brief Wrapped class template. */ + using class_type = meta_class_template_tag; + /*! @brief List of template arguments. */ + using args_type = type_list; +}; + +} // namespace entt + +#endif + +// #include "meta/type_traits.hpp" +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + +#include +#include + +namespace entt { + +/** + * @brief Traits class template to be specialized to enable support for meta + * template information. + */ +template +struct meta_template_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * sequence containers. + */ +template +struct meta_sequence_container_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * associative containers. + */ +template +struct meta_associative_container_traits; + +/** + * @brief Provides the member constant `value` to true if a given type is a + * pointer-like type from the point of view of the meta system, false otherwise. + */ +template +struct is_meta_pointer_like: std::false_type {}; + +/** + * @brief Partial specialization to ensure that const pointer-like types are + * also accepted. + * @tparam Type Potentially pointer-like type. + */ +template +struct is_meta_pointer_like: is_meta_pointer_like {}; + +/** + * @brief Helper variable template. + * @tparam Type Potentially pointer-like type. + */ +template +inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value; + +} // namespace entt + +#endif + +// #include "meta/utility.hpp" +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + +#include +#include +#include +#include +// #include "../core/type_traits.hpp" + +// #include "../locator/locator.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" + + +namespace entt { + +/** + * @brief Meta function descriptor traits. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + * @tparam Static Function staticness. + * @tparam Const Function constness. + */ +template +struct meta_function_descriptor_traits { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = Args; + + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr bool is_static = Static; + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr bool is_const = Const; +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct meta_function_descriptor; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + true> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta data is associated. + * @tparam Class Actual owner of the data member. + * @tparam Ret Data member type. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret &, + std::conditional_t, type_list<>, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam MaybeType First function argument. + * @tparam Args Other function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t< + std::is_same_v>, Type> || std::is_base_of_v>, Type>, + type_list, + type_list>, + !(std::is_same_v>, Type> || std::is_base_of_v>, Type>), + std::is_const_v> && (std::is_same_v>, Type> || std::is_base_of_v>, Type>)> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + type_list<>, + true, + false> {}; + +/** + * @brief Meta function helper. + * + * Converts a function type to be associated with a reflected type into its meta + * function descriptor. + * + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +class meta_function_helper { + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...) const); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret Class::*); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Class); + +public: + /*! @brief The meta function descriptor of the given function. */ + using type = decltype(get_rid_of_noexcept(std::declval())); +}; + +/** + * @brief Helper type. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +using meta_function_helper_t = typename meta_function_helper::type; + +/** + * @brief Wraps a value depending on the given policy. + * + * This function always returns a wrapped value in the requested context.
+ * Therefore, if the passed value is itself a wrapped object with a different + * context, it undergoes a rebinding to the requested context. + * + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param ctx The context from which to search for meta types. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { + if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type, value}; + } else if constexpr(std::is_same_v) { + static_assert(std::is_lvalue_reference_v, "Invalid type"); + return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; + } else { + return meta_any{ctx, std::forward(value)}; + } +} + +/** + * @brief Wraps a value depending on the given policy. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(Type &&value) { + return meta_dispatch(locator::value_or(), std::forward(value)); +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param ctx The context from which to search for meta types. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::meta_arg_node(context, Type{}, index)}; +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept { + return meta_arg(locator::value_or(), index); +} + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { + if constexpr(!std::is_same_v && !std::is_same_v) { + if constexpr(std::is_member_function_pointer_v || std::is_function_v>>) { + using descriptor = meta_function_helper_t; + using data_type = type_list_element_t; + + if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { + std::invoke(Data, *clazz, value.cast()); + return true; + } + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_reference_t::return_type>; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { + std::invoke(Data, *clazz) = value.cast(); + return true; + } + } + } else { + using data_type = std::remove_reference_t; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(value.allow_cast()) { + *Data = value.cast(); + return true; + } + } + } + } + + return false; +} + +/** + * @brief Gets the value of a given variable. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) { + if constexpr(std::is_member_pointer_v || std::is_function_v>>) { + if constexpr(!std::is_array_v>>>) { + if constexpr(std::is_invocable_v) { + if(auto *clazz = instance->try_cast(); clazz) { + return meta_dispatch(ctx, std::invoke(Data, *clazz)); + } + } + + if constexpr(std::is_invocable_v) { + if(auto *fallback = instance->try_cast(); fallback) { + return meta_dispatch(ctx, std::invoke(Data, *fallback)); + } + } + } + + return meta_any{meta_ctx_arg, ctx}; + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{meta_ctx_arg, ctx}; + } else { + return meta_dispatch(ctx, *Data); + } + } else { + return meta_dispatch(ctx, Data); + } +} + +/** + * @brief Gets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(meta_handle instance) { + return meta_getter(locator::value_or(), std::move(instance)); +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { + if constexpr(std::is_void_v(candidate), args...))>) { + std::invoke(std::forward(candidate), args...); + return meta_any{ctx, std::in_place_type}; + } else { + return meta_dispatch(ctx, std::invoke(std::forward(candidate), args...)); + } +} + +template +[[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence) { + using descriptor = meta_function_helper_t>; + + if constexpr(std::is_invocable_v, const Type &, type_list_element_t...>) { + if(const auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else if constexpr(std::is_invocable_v, Type &, type_list_element_t...>) { + if(auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { // NOLINT + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else { + if(((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(ctx, std::forward(candidate), (args + Index)->cast>()...); + } + } + + return meta_any{meta_ctx_arg, ctx}; +} + +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence) { + if(((args + Index)->allow_cast() && ...)) { + return meta_any{ctx, std::in_place_type, (args + Index)->cast()...}; + } + + return meta_any{meta_ctx_arg, ctx}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), std::forward(candidate), args); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args) { + return internal::meta_construct(ctx, args, std::index_sequence_for{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param ctx The context from which to search for meta types. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { + if constexpr(meta_function_helper_t::is_static || std::is_class_v>>) { + return internal::meta_invoke(ctx, {}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); + } else { + return internal::meta_invoke(ctx, *args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); + } +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { + return meta_construct(locator::value_or(), std::forward(candidate), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { + return meta_construct(ctx, Candidate, args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +} // namespace entt + +#endif + +// #include "platform/android-ndk-r17.hpp" +#ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP +#define ENTT_PLATFORM_ANDROID_NDK_R17_HPP + +/*! @cond TURN_OFF_DOXYGEN */ +#ifdef __ANDROID__ +# include +# if __NDK_MAJOR__ == 17 + +# include +# include +# include + +namespace std { + +namespace internal { + +template +constexpr auto is_invocable(int) -> decltype(std::invoke(std::declval(), std::declval()...), std::true_type{}); + +template +constexpr std::false_type is_invocable(...); + +template +constexpr auto is_invocable_r(int) +-> std::enable_if_t(), std::declval()...)), Ret>, std::true_type>; + + +template +constexpr std::false_type is_invocable_r(...); + +} // namespace internal + +template +struct is_invocable: decltype(internal::is_invocable(0)) {}; + +template +inline constexpr bool is_invocable_v = std::is_invocable::value; + +template +struct is_invocable_r: decltype(internal::is_invocable_r(0)) {}; + +template +inline constexpr bool is_invocable_r_v = std::is_invocable_r::value; + +template +struct invoke_result { + using type = decltype(std::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result_t = typename std::invoke_result::type; + +} // namespace std + +# endif +#endif +/*! @endcond */ + +#endif + +// #include "poly/poly.hpp" +#ifndef ENTT_POLY_POLY_HPP +#define ENTT_POLY_POLY_HPP + +#include +#include +#include +#include +#include +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #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 "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr; + size_type length; + hash_type hash; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers 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. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{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. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> 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 +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @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 differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @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 first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @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 first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @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 first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @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 first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) 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; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, the object owns the contained element. */ + owner, + /*! @brief Aliasing mode, the object _points_ to a non-const element. */ + ref, + /*! @brief Const aliasing mode, the object _points_ to a const element. */ + cref +}; + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + using operation = internal::any_operation; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; + + template + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { + static_assert(!std::is_void_v && std::is_same_v>, Type>, "Invalid type"); + const Type *element = nullptr; + + if constexpr(in_situ) { + element = (value.mode == any_policy::owner) ? reinterpret_cast(&value.storage) : static_cast(value.instance); + } else { + element = static_cast(value.instance); + } + + switch(op) { + case operation::copy: + if constexpr(std::is_copy_constructible_v) { + static_cast(const_cast(other))->initialize(*element); + } + break; + case operation::move: + if constexpr(in_situ) { + if(value.mode == any_policy::owner) { + return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; + } + } + + return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); + case operation::transfer: + if constexpr(std::is_move_assignable_v) { + *const_cast(element) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case operation::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(element) = *static_cast(other); + return other; + } + break; + case operation::destroy: + if constexpr(in_situ) { + element->~Type(); + } else if constexpr(std::is_array_v) { + delete[] element; + } else { + delete element; + } + break; + case operation::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return *element == *static_cast(other) ? other : nullptr; + } else { + return (element == other) ? other : nullptr; + } + case operation::get: + return element; + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + + if constexpr(!std::is_void_v) { + vtable = basic_vtable>>; + + if constexpr(std::is_lvalue_reference_v) { + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + instance = (std::addressof(args), ...); + } else if constexpr(in_situ>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; + } else { + new(&storage) std::remove_cv_t>(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + instance = new std::remove_cv_t>{std::forward(args)...}; + } else { + instance = new std::remove_cv_t>(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const any_policy pol) noexcept + : instance{other.data()}, + info{other.info}, + vtable{other.vtable}, + mode{pol} {} + +public: + /*! @brief Size of the internal storage. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : instance{}, + info{}, + vtable{}, + mode{any_policy::owner} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{} { + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : instance{}, + info{other.info}, + vtable{other.vtable}, + mode{other.mode} { + if(other.vtable) { + other.vtable(operation::move, other, this); + } + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + reset(); + + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + reset(); + + if(other.vtable) { + other.vtable(operation::move, other, this); + info = other.info; + vtable = other.vtable; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the object type if any, `type_id()` otherwise. + * @return The object type if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return *info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return vtable ? vtable(operation::get, *this, nullptr) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return *info == req ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @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) { + reset(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(basic_any &&other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + if(auto *val = other.data(); val) { + return (vtable(operation::transfer, *this, val) != nullptr); + } else { + return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); + } + } + + return false; + } + + /*! @brief Destroys contained object */ + void reset() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); + info = &type_id(); + vtable = nullptr; + mode = any_policy::owner; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return vtable != nullptr; + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + return basic_any{*this, any_policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::owner); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + any_policy mode; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &data) noexcept { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } else { + return any_cast(data); + } + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + const auto &info = type_id>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_POLY_FWD_HPP +#define ENTT_POLY_FWD_HPP + +#include + +namespace entt { + +template +class basic_poly; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Concept Concept descriptor. + */ +template +using poly = basic_poly; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Inspector class used to infer the type of the virtual table. */ +struct poly_inspector { + /** + * @brief Generic conversion operator (definition only). + * @tparam Type Type to which conversion is requested. + */ + template + operator Type &&() const; + + /** + * @brief Dummy invocation function (definition only). + * @tparam Member Index of the function to invoke. + * @tparam Args Types of arguments to pass to the function. + * @param args The arguments to pass to the function. + * @return A poly inspector convertible to any type. + */ + template + poly_inspector invoke(Args &&...args) const; + + /*! @copydoc invoke */ + template + poly_inspector invoke(Args &&...args); +}; + +/** + * @brief Static virtual table factory. + * @tparam Concept Concept descriptor. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + */ +template +class poly_vtable { + using inspector = typename Concept::template type; + + template + static auto vtable_entry(Ret (*)(inspector &, Args...)) -> Ret (*)(basic_any &, Args...); + + template + static auto vtable_entry(Ret (*)(const inspector &, Args...)) -> Ret (*)(const basic_any &, Args...); + + template + static auto vtable_entry(Ret (*)(Args...)) -> Ret (*)(const basic_any &, Args...); + + template + static auto vtable_entry(Ret (inspector::*)(Args...)) -> Ret (*)(basic_any &, Args...); + + template + static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any &, Args...); + + template + static auto make_vtable(value_list) noexcept + -> decltype(std::make_tuple(vtable_entry(Candidate)...)); + + template + [[nodiscard]] static constexpr auto make_vtable(type_list) noexcept { + if constexpr(sizeof...(Func) == 0u) { + return decltype(make_vtable(typename Concept::template impl{})){}; + } else if constexpr((std::is_function_v && ...)) { + return decltype(std::make_tuple(vtable_entry(std::declval())...)){}; + } + } + + template + static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) noexcept { + if constexpr(std::is_invocable_r_v) { + entry = +[](Any &, Args... args) -> Ret { + return std::invoke(Candidate, std::forward(args)...); + }; + } else { + entry = +[](Any &instance, Args... args) -> Ret { + return static_cast(std::invoke(Candidate, any_cast &>(instance), std::forward(args)...)); + }; + } + } + + template + [[nodiscard]] static auto fill_vtable(std::index_sequence) noexcept { + vtable_type impl{}; + (fill_vtable_entry>>(std::get(impl)), ...); + return impl; + } + + using vtable_type = decltype(make_vtable(Concept{})); + static constexpr bool is_mono_v = std::tuple_size_v == 1u; + +public: + /*! @brief Virtual table type. */ + using type = std::conditional_t, const vtable_type *>; + + /** + * @brief Returns a static virtual table for a specific concept and type. + * @tparam Type The type for which to generate the virtual table. + * @return A static virtual table for the given concept and type. + */ + template + [[nodiscard]] static type instance() noexcept { + static_assert(std::is_same_v>, "Type differs from its decayed form"); + static const vtable_type vtable = fill_vtable(std::make_index_sequence::size>{}); + + if constexpr(is_mono_v) { + return std::get<0>(vtable); + } else { + return &vtable; + } + } +}; + +/** + * @brief Poly base class used to inject functionalities into concepts. + * @tparam Poly The outermost poly class. + */ +template +struct poly_base { + /** + * @brief Invokes a function from the static virtual table. + * @tparam Member Index of the function to invoke. + * @tparam Args Types of arguments to pass to the function. + * @param self A reference to the poly object that made the call. + * @param args The arguments to pass to the function. + * @return The return value of the invoked function, if any. + */ + template + [[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&...args) const { + const auto &poly = static_cast(self); + + if constexpr(std::is_function_v>) { + return poly.vtable(poly.storage, std::forward(args)...); + } else { + return std::get(*poly.vtable)(poly.storage, std::forward(args)...); + } + } + + /*! @copydoc invoke */ + template + [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&...args) { + auto &poly = static_cast(self); + + if constexpr(std::is_function_v>) { + static_assert(Member == 0u, "Unknown member"); + return poly.vtable(poly.storage, std::forward(args)...); + } else { + return std::get(*poly.vtable)(poly.storage, std::forward(args)...); + } + } +}; + +/** + * @brief Shortcut for calling `poly_base::invoke`. + * @tparam Member Index of the function to invoke. + * @tparam Poly A fully defined poly object. + * @tparam Args Types of arguments to pass to the function. + * @param self A reference to the poly object that made the call. + * @param args The arguments to pass to the function. + * @return The return value of the invoked function, if any. + */ +template +decltype(auto) poly_call(Poly &&self, Args &&...args) { + return std::forward(self).template invoke(self, std::forward(args)...); +} + +/** + * @brief Static polymorphism made simple and within everyone's reach. + * + * Static polymorphism is a very powerful tool in C++, albeit sometimes + * cumbersome to obtain.
+ * This class aims to make it simple and easy to use. + * + * @note + * Both deduced and defined static virtual tables are supported.
+ * Moreover, the `poly` class template also works with unmanaged objects. + * + * @tparam Concept Concept descriptor. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_poly: private Concept::template type>> { + friend struct poly_base; + +public: + /*! @brief Concept type. */ + using concept_type = typename Concept::template type>; + /*! @brief Virtual table type. */ + using vtable_type = typename poly_vtable::type; + + /*! @brief Default constructor. */ + basic_poly() noexcept + : storage{}, + vtable{} {} + + /** + * @brief Constructs a poly by directly initializing the new object. + * @tparam Type Type of object to use to initialize the poly. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_poly(std::in_place_type_t, Args &&...args) + : storage{std::in_place_type, std::forward(args)...}, + vtable{poly_vtable::template instance>>()} {} + + /** + * @brief Constructs a poly from a given value. + * @tparam Type Type of object to use to initialize the poly. + * @param value An instance of an object to use to initialize the poly. + */ + template>, basic_poly>>> + basic_poly(Type &&value) noexcept + : basic_poly{std::in_place_type>>, std::forward(value)} {} + + /** + * @brief Returns the object type if any, `type_id()` otherwise. + * @return The object type if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return storage.type(); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return storage.data(); + } + + /*! @copydoc data */ + [[nodiscard]] void *data() noexcept { + return storage.data(); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the poly. + * @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) { + storage.template emplace(std::forward(args)...); + vtable = poly_vtable::template instance>>(); + } + + /*! @brief Destroys contained object */ + void reset() { + storage.reset(); + vtable = {}; + } + + /** + * @brief Returns false if a poly is empty, true otherwise. + * @return False if the poly is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(storage); + } + + /** + * @brief Returns a pointer to the underlying concept. + * @return A pointer to the underlying concept. + */ + [[nodiscard]] concept_type *operator->() noexcept { + return this; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const concept_type *operator->() const noexcept { + return this; + } + + /** + * @brief Aliasing constructor. + * @return A poly that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_poly as_ref() noexcept { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_poly as_ref() const noexcept { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + +private: + basic_any storage; + vtable_type vtable; +}; + +} // namespace entt + +#endif + +// #include "process/process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_PROCESS_FWD_HPP +#define ENTT_PROCESS_FWD_HPP + +#include +#include + +namespace entt { + +template +class process; + +template> +class basic_scheduler; + +/*! @brief Alias declaration for the most common use case. */ +using scheduler = basic_scheduler<>; + +} // namespace entt + +#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 : std::uint8_t { + uninitialized = 0, + running, + paused, + succeeded, + failed, + aborted, + finished, + rejected + }; + + template + auto next(std::integral_constant) + -> decltype(std::declval().init(), void()) { + static_cast(this)->init(); + } + + template + auto next(std::integral_constant, Delta delta, void *data) + -> decltype(std::declval().update(delta, data), void()) { + static_cast(this)->update(delta, data); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().succeeded(), void()) { + static_cast(this)->succeeded(); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().failed(), void()) { + static_cast(this)->failed(); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().aborted(), void()) { + static_cast(this)->aborted(); + } + + void next(...) const 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() 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() 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() 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() 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() noexcept { + static_assert(std::is_base_of_v, "Incorrect use of the class template"); + } + + /** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediate Requests an immediate operation. + */ + void abort(const bool immediate = false) { + if(alive()) { + current = state::aborted; + + if(immediate) { + tick({}); + } + } + } + + /** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ + [[nodiscard]] bool alive() const 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. + */ + [[nodiscard]] bool finished() const noexcept { + return current == state::finished; + } + + /** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ + [[nodiscard]] bool paused() const noexcept { + return current == state::paused; + } + + /** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ + [[nodiscard]] bool rejected() const noexcept { + return current == state::rejected; + } + + /** + * @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(std::integral_constant{}); + current = state::running; + break; + case state::running: + next(std::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(std::integral_constant{}); + current = state::finished; + break; + case state::failed: + next(std::integral_constant{}); + current = state::rejected; + break; + case state::aborted: + next(std::integral_constant{}); + current = state::rejected; + break; + default: + // suppress warnings + break; + } + } + +private: + state current{state::uninitialized}; +}; + +/** + * @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(); }); + } +}; + +} // namespace entt + +#endif + +// #include "process/scheduler.hpp" +#ifndef ENTT_PROCESS_SCHEDULER_HPP +#define ENTT_PROCESS_SCHEDULER_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "fwd.hpp" + +// #include "process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#include +#include +#include +// #include "fwd.hpp" + + +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 : std::uint8_t { + uninitialized = 0, + running, + paused, + succeeded, + failed, + aborted, + finished, + rejected + }; + + template + auto next(std::integral_constant) + -> decltype(std::declval().init(), void()) { + static_cast(this)->init(); + } + + template + auto next(std::integral_constant, Delta delta, void *data) + -> decltype(std::declval().update(delta, data), void()) { + static_cast(this)->update(delta, data); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().succeeded(), void()) { + static_cast(this)->succeeded(); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().failed(), void()) { + static_cast(this)->failed(); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().aborted(), void()) { + static_cast(this)->aborted(); + } + + void next(...) const 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() 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() 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() 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() 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() noexcept { + static_assert(std::is_base_of_v, "Incorrect use of the class template"); + } + + /** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediate Requests an immediate operation. + */ + void abort(const bool immediate = false) { + if(alive()) { + current = state::aborted; + + if(immediate) { + tick({}); + } + } + } + + /** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ + [[nodiscard]] bool alive() const 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. + */ + [[nodiscard]] bool finished() const noexcept { + return current == state::finished; + } + + /** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ + [[nodiscard]] bool paused() const noexcept { + return current == state::paused; + } + + /** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ + [[nodiscard]] bool rejected() const noexcept { + return current == state::rejected; + } + + /** + * @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(std::integral_constant{}); + current = state::running; + break; + case state::running: + next(std::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(std::integral_constant{}); + current = state::finished; + break; + case state::failed: + next(std::integral_constant{}); + current = state::rejected; + break; + case state::aborted: + next(std::integral_constant{}); + current = state::rejected; + break; + default: + // suppress warnings + break; + } + } + +private: + state current{state::uninitialized}; +}; + +/** + * @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(); }); + } +}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct basic_process_handler { + virtual ~basic_process_handler() = default; + + virtual bool update(const Delta, void *) = 0; + virtual void abort(const bool) = 0; + + // std::shared_ptr because of its type erased allocator which is useful here + std::shared_ptr next; +}; + +template +struct process_handler final: basic_process_handler { + template + process_handler(Args &&...args) + : process{std::forward(args)...} {} + + bool update(const Delta delta, void *data) override { + if(process.tick(delta, data); process.rejected()) { + this->next.reset(); + } + + return (process.rejected() || process.finished()); + } + + void abort(const bool immediate) override { + process.abort(immediate); + } + + Type process; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_scheduler { + template + using handler_type = internal::process_handler; + + // std::shared_ptr because of its type erased allocator which is useful here + using process_type = std::shared_ptr>; + + using alloc_traits = std::allocator_traits; + using container_allocator = typename alloc_traits::template rebind_alloc; + using container_type = std::vector; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Unsigned integer type. */ + using delta_type = Delta; + + /*! @brief Default constructor. */ + basic_scheduler() + : basic_scheduler{allocator_type{}} {} + + /** + * @brief Constructs a scheduler with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_scheduler(const allocator_type &allocator) + : handlers{allocator, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_scheduler(basic_scheduler &&other) noexcept + : handlers{std::move(other.handlers)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_scheduler(basic_scheduler &&other, const allocator_type &allocator) noexcept + : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed"); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This scheduler. + */ + basic_scheduler &operator=(basic_scheduler &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed"); + handlers = std::move(other.handlers); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given scheduler. + * @param other Scheduler to exchange the content with. + */ + void swap(basic_scheduler &other) { + using std::swap; + swap(handlers, other.handlers); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return handlers.second(); + } + + /** + * @brief Number of processes currently scheduled. + * @return Number of processes currently scheduled. + */ + [[nodiscard]] size_type size() const noexcept { + return handlers.first().size(); + } + + /** + * @brief Returns true if at least a process is currently scheduled. + * @return True if there are scheduled processes, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return handlers.first().empty(); + } + + /** + * @brief Discards all scheduled processes. + * + * Processes aren't aborted. They are discarded along with their children + * and never executed again. + */ + void clear() { + handlers.first().clear(); + } + + /** + * @brief Schedules a process for the next tick. + * + * Returned value can be used to attach a continuation for the last process. + * The continutation is scheduled automatically 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 This process scheduler. + */ + template + basic_scheduler &attach(Args &&...args) { + static_assert(std::is_base_of_v, Proc>, "Invalid process type"); + auto &ref = handlers.first().emplace_back(std::allocate_shared>(handlers.second(), std::forward(args)...)); + // forces the process to exit the uninitialized state + ref->update({}, nullptr); + return *this; + } + + /** + * @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 can be used to attach a continuation for the last process. + * The continutation is scheduled automatically 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 This process scheduler. + */ + template + basic_scheduler &attach(Func &&func) { + using Proc = process_adaptor, Delta>; + return attach(std::forward(func)); + } + + /** + * @brief Sets a process as a continuation of the last scheduled process. + * @tparam Proc Type of process to use as a continuation. + * @tparam Args Types of arguments to use to initialize the process. + * @param args Parameters to use to initialize the process. + * @return This process scheduler. + */ + template + basic_scheduler &then(Args &&...args) { + static_assert(std::is_base_of_v, Proc>, "Invalid process type"); + ENTT_ASSERT(!handlers.first().empty(), "Process not available"); + auto *curr = handlers.first().back().get(); + for(; curr->next; curr = curr->next.get()) {} + curr->next = std::allocate_shared>(handlers.second(), std::forward(args)...); + return *this; + } + + /** + * @brief Sets a process as a continuation of the last scheduled process. + * @tparam Func Type of process to use as a continuation. + * @param func Either a lambda or a functor to use as a process. + * @return This process scheduler. + */ + template + basic_scheduler &then(Func &&func) { + using Proc = process_adaptor, Delta>; + return then(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_type delta, void *data = nullptr) { + for(auto next = handlers.first().size(); next; --next) { + if(const auto pos = next - 1u; handlers.first()[pos]->update(delta, data)) { + // updating might spawn/reallocate, cannot hold refs until here + if(auto &curr = handlers.first()[pos]; curr->next) { + curr = std::move(curr->next); + // forces the process to exit the uninitialized state + curr->update({}, nullptr); + } else { + curr = std::move(handlers.first().back()); + handlers.first().pop_back(); + } + } + } + } + + /** + * @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 immediate Requests an immediate operation. + */ + void abort(const bool immediate = false) { + for(auto &&curr: handlers.first()) { + curr->abort(immediate); + } + } + +private: + compressed_pair handlers; +}; + +} // namespace entt + +#endif + +// #include "resource/cache.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP +#define ENTT_RESOURCE_RESOURCE_CACHE_HPP + +#include +#include +#include +#include +#include +#include +#include +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_RESOURCE_FWD_HPP +#define ENTT_RESOURCE_FWD_HPP + +#include + +namespace entt { + +template +struct resource_loader; + +template, typename = std::allocator> +class resource_cache; + +template +class resource; + +} // namespace entt + +#endif + +// #include "loader.hpp" +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Transparent loader for shared resources. + * @tparam Type Type of resources created by the loader. + */ +template +struct resource_loader { + /*! @brief Result type. */ + using result_type = std::shared_ptr; + + /** + * @brief Constructs a shared pointer to a resource from its arguments. + * @tparam Args Types of arguments to use to construct the resource. + * @param args Parameters to use to construct the resource. + * @return A shared pointer to a resource of the given type. + */ + template + result_type operator()(Args &&...args) const { + return std::make_shared(std::forward(args)...); + } +}; + +} // namespace entt + +#endif + +// #include "resource.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_HPP +#define ENTT_RESOURCE_RESOURCE_HPP + +#include +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Basic resource handle. + * + * A handle wraps a resource and extends its lifetime. It also shares the same + * resource with all other handles constructed from the same element.
+ * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to push references around. + * + * @tparam Type Type of resource managed by a handle. + */ +template +class resource { + template + friend class resource; + + template + static constexpr bool is_acceptable_v = !std::is_same_v && std::is_constructible_v; + +public: + /*! @brief Resource type. */ + using element_type = Type; + /*! @brief Handle type. */ + using handle_type = std::shared_ptr; + + /*! @brief Default constructor. */ + resource() noexcept + : value{} {} + + /** + * @brief Creates a handle from a weak pointer, namely a resource. + * @param res A weak pointer to a resource. + */ + explicit resource(handle_type res) noexcept + : value{std::move(res)} {} + + /*! @brief Default copy constructor. */ + resource(const resource &) noexcept = default; + + /*! @brief Default move constructor. */ + resource(resource &&) noexcept = default; + + /** + * @brief Aliasing constructor. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle with which to share ownership information. + * @param res Unrelated and unmanaged resources. + */ + template + resource(const resource &other, element_type &res) noexcept + : value{other.value, std::addressof(res)} {} + + /** + * @brief Copy constructs a handle which shares ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + */ + template>> + resource(const resource &other) noexcept + : value{other.value} {} + + /** + * @brief Move constructs a handle which takes ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + */ + template>> + resource(resource &&other) noexcept + : value{std::move(other.value)} {} + + /** + * @brief Default copy assignment operator. + * @return This resource handle. + */ + resource &operator=(const resource &) noexcept = default; + + /** + * @brief Default move assignment operator. + * @return This resource handle. + */ + resource &operator=(resource &&) noexcept = default; + + /** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + * @return This resource handle. + */ + template + std::enable_if_t, resource &> + operator=(const resource &other) noexcept { + value = other.value; + return *this; + } + + /** + * @brief Move assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + * @return This resource handle. + */ + template + std::enable_if_t, resource &> + operator=(resource &&other) noexcept { + value = std::move(other.value); + return *this; + } + + /** + * @brief Returns a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource. + * + * @return A reference to the managed resource. + */ + [[nodiscard]] element_type &operator*() const noexcept { + return *value; + } + + /*! @copydoc operator* */ + [[nodiscard]] operator element_type &() const noexcept { + return *value; + } + + /** + * @brief Returns a pointer to the managed resource. + * @return A pointer to the managed resource. + */ + [[nodiscard]] element_type *operator->() const noexcept { + return value.get(); + } + + /** + * @brief Returns true if a handle contains a resource, false otherwise. + * @return True if the handle contains a resource, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(value); + } + + /** + * @brief Returns the underlying resource handle. + * @return The underlying resource handle. + */ + [[nodiscard]] const handle_type &handle() const noexcept { + return value; + } + +private: + handle_type value; +}; + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same resource, false otherwise. + */ +template +[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) == std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same resource, true otherwise. + */ +template +[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than the second, false otherwise. + */ +template +[[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) < std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than the second, false otherwise. + */ +template +[[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs > rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class resource_cache_iterator final { + template + friend class resource_cache_iterator; + +public: + using value_type = std::pair>; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr resource_cache_iterator() noexcept = default; + + constexpr resource_cache_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr resource_cache_iterator(const resource_cache_iterator, Other> &other) noexcept + : it{other.it} {} + + constexpr resource_cache_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr resource_cache_iterator operator++(int) noexcept { + resource_cache_iterator orig = *this; + return ++(*this), orig; + } + + constexpr resource_cache_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr resource_cache_iterator operator--(int) noexcept { + resource_cache_iterator orig = *this; + return operator--(), orig; + } + + constexpr resource_cache_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr resource_cache_iterator operator+(const difference_type value) const noexcept { + resource_cache_iterator copy = *this; + return (copy += value); + } + + constexpr resource_cache_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr resource_cache_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, resource{it[value].second}}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return (*this)[0]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template + friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + + template + friend constexpr bool operator==(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + + template + friend constexpr bool operator<(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic cache for resources of any type. + * @tparam Type Type of resources managed by a cache. + * @tparam Loader Type of loader used to create the resources. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class resource_cache { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_allocator = typename alloc_traits::template rebind_alloc>; + using container_type = dense_map, container_allocator>; + +public: + /*! @brief Resource type. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Loader type. */ + using loader_type = Loader; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::resource_cache_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::resource_cache_iterator; + + /*! @brief Default constructor. */ + resource_cache() + : resource_cache{loader_type{}} {} + + /** + * @brief Constructs an empty cache with a given allocator. + * @param allocator The allocator to use. + */ + explicit resource_cache(const allocator_type &allocator) + : resource_cache{loader_type{}, allocator} {} + + /** + * @brief Constructs an empty cache with a given allocator and loader. + * @param callable The loader to use. + * @param allocator The allocator to use. + */ + explicit resource_cache(const loader_type &callable, const allocator_type &allocator = allocator_type{}) + : pool{container_type{allocator}, callable} {} + + /*! @brief Default copy constructor. */ + resource_cache(const resource_cache &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + resource_cache(const resource_cache &other, const allocator_type &allocator) + : pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {} + + /*! @brief Default move constructor. */ + resource_cache(resource_cache &&) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + resource_cache(resource_cache &&other, const allocator_type &allocator) + : pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {} + + /** + * @brief Default copy assignment operator. + * @return This cache. + */ + resource_cache &operator=(const resource_cache &) = default; + + /** + * @brief Default move assignment operator. + * @return This cache. + */ + resource_cache &operator=(resource_cache &&) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pool.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the cache is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal cache. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return pool.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return pool.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal cache. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return pool.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return pool.first().end(); + } + + /** + * @brief Returns true if a cache contains no resources, false otherwise. + * @return True if the cache contains no resources, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return pool.first().empty(); + } + + /** + * @brief Number of resources managed by a cache. + * @return Number of resources currently stored. + */ + [[nodiscard]] size_type size() const noexcept { + return pool.first().size(); + } + + /*! @brief Clears a cache. */ + void clear() noexcept { + pool.first().clear(); + } + + /** + * @brief Loads a resource, if its identifier does not exist. + * + * Arguments are forwarded directly to the loader and _consumed_ only if the + * resource doesn't already exist. + * + * @warning + * If the resource isn't loaded correctly, the returned handle could be + * invalid and any use of it will result in undefined behavior. + * + * @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 pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair load(const id_type id, Args &&...args) { + if(auto it = pool.first().find(id); it != pool.first().end()) { + return {it, false}; + } + + return pool.first().emplace(id, pool.second()(std::forward(args)...)); + } + + /** + * @brief Force loads a resource, if its identifier does not exist. + * @copydetails load + */ + template + std::pair force_load(const id_type id, Args &&...args) { + return {pool.first().insert_or_assign(id, pool.second()(std::forward(args)...)).first, true}; + } + + /** + * @brief Returns a handle for a given resource identifier. + * + * @warning + * There is no guarantee that the returned handle is valid.
+ * If it is not, any use will result in indefinite behavior. + * + * @param id Unique resource identifier. + * @return A handle for the given resource. + */ + [[nodiscard]] resource operator[](const id_type id) const { + if(auto it = pool.first().find(id); it != pool.first().cend()) { + return resource{it->second}; + } + + return {}; + } + + /*! @copydoc operator[] */ + [[nodiscard]] resource operator[](const id_type id) { + if(auto it = pool.first().find(id); it != pool.first().end()) { + return resource{it->second}; + } + + return {}; + } + + /** + * @brief Checks if a cache contains a given identifier. + * @param id Unique resource identifier. + * @return True if the cache contains the resource, false otherwise. + */ + [[nodiscard]] bool contains(const id_type id) const { + return pool.first().contains(id); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto it = pool.first().begin(); + return pool.first().erase(it + (pos - const_iterator{it})); + } + + /** + * @brief Removes the given elements from a cache. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto it = pool.first().begin(); + return pool.first().erase(it + (first - const_iterator{it}), it + (last - const_iterator{it})); + } + + /** + * @brief Removes the given elements from a cache. + * @param id Unique resource identifier. + * @return Number of resources erased (either 0 or 1). + */ + size_type erase(const id_type id) { + return pool.first().erase(id); + } + + /** + * @brief Returns the loader used to create resources. + * @return The loader used to create resources. + */ + [[nodiscard]] loader_type loader() const { + return pool.second(); + } + +private: + compressed_pair pool; +}; + +} // namespace entt + +#endif + +// #include "resource/loader.hpp" +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Transparent loader for shared resources. + * @tparam Type Type of resources created by the loader. + */ +template +struct resource_loader { + /*! @brief Result type. */ + using result_type = std::shared_ptr; + + /** + * @brief Constructs a shared pointer to a resource from its arguments. + * @tparam Args Types of arguments to use to construct the resource. + * @param args Parameters to use to construct the resource. + * @return A shared pointer to a resource of the given type. + */ + template + result_type operator()(Args &&...args) const { + return std::make_shared(std::forward(args)...); + } +}; + +} // namespace entt + +#endif + +// #include "resource/resource.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_HPP +#define ENTT_RESOURCE_RESOURCE_HPP + +#include +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Basic resource handle. + * + * A handle wraps a resource and extends its lifetime. It also shares the same + * resource with all other handles constructed from the same element.
+ * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to push references around. + * + * @tparam Type Type of resource managed by a handle. + */ +template +class resource { + template + friend class resource; + + template + static constexpr bool is_acceptable_v = !std::is_same_v && std::is_constructible_v; + +public: + /*! @brief Resource type. */ + using element_type = Type; + /*! @brief Handle type. */ + using handle_type = std::shared_ptr; + + /*! @brief Default constructor. */ + resource() noexcept + : value{} {} + + /** + * @brief Creates a handle from a weak pointer, namely a resource. + * @param res A weak pointer to a resource. + */ + explicit resource(handle_type res) noexcept + : value{std::move(res)} {} + + /*! @brief Default copy constructor. */ + resource(const resource &) noexcept = default; + + /*! @brief Default move constructor. */ + resource(resource &&) noexcept = default; + + /** + * @brief Aliasing constructor. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle with which to share ownership information. + * @param res Unrelated and unmanaged resources. + */ + template + resource(const resource &other, element_type &res) noexcept + : value{other.value, std::addressof(res)} {} + + /** + * @brief Copy constructs a handle which shares ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + */ + template>> + resource(const resource &other) noexcept + : value{other.value} {} + + /** + * @brief Move constructs a handle which takes ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + */ + template>> + resource(resource &&other) noexcept + : value{std::move(other.value)} {} + + /** + * @brief Default copy assignment operator. + * @return This resource handle. + */ + resource &operator=(const resource &) noexcept = default; + + /** + * @brief Default move assignment operator. + * @return This resource handle. + */ + resource &operator=(resource &&) noexcept = default; + + /** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + * @return This resource handle. + */ + template + std::enable_if_t, resource &> + operator=(const resource &other) noexcept { + value = other.value; + return *this; + } + + /** + * @brief Move assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + * @return This resource handle. + */ + template + std::enable_if_t, resource &> + operator=(resource &&other) noexcept { + value = std::move(other.value); + return *this; + } + + /** + * @brief Returns a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource. + * + * @return A reference to the managed resource. + */ + [[nodiscard]] element_type &operator*() const noexcept { + return *value; + } + + /*! @copydoc operator* */ + [[nodiscard]] operator element_type &() const noexcept { + return *value; + } + + /** + * @brief Returns a pointer to the managed resource. + * @return A pointer to the managed resource. + */ + [[nodiscard]] element_type *operator->() const noexcept { + return value.get(); + } + + /** + * @brief Returns true if a handle contains a resource, false otherwise. + * @return True if the handle contains a resource, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(value); + } + + /** + * @brief Returns the underlying resource handle. + * @return The underlying resource handle. + */ + [[nodiscard]] const handle_type &handle() const noexcept { + return value; + } + +private: + handle_type value; +}; + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same resource, false otherwise. + */ +template +[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) == std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same resource, true otherwise. + */ +template +[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than the second, false otherwise. + */ +template +[[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) < std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than the second, false otherwise. + */ +template +[[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs > rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace entt + +#endif + +// #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 + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + +#include + +namespace entt { + +template +class delegate; + +template> +class basic_dispatcher; + +template> +class emitter; + +class connection; + +struct scoped_connection; + +template +class sink; + +template> +class sigh; + +/*! @brief Alias declaration for the most common use case. */ +using dispatcher = basic_dispatcher<>; + +/*! @brief Disambiguation tag for constructors and the like. */ +template +struct connect_arg_t { + /*! @brief Default constructor. */ + explicit connect_arg_t() = default; +}; + +/** + * @brief Constant of type connect_arg_t used to disambiguate calls. + * @tparam Candidate Element to connect (likely a free or member function). + */ +template +inline constexpr connect_arg_t connect_arg{}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 { + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() noexcept + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) noexcept { + connect(function, payload); + } + + /** + * @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() noexcept { + instance = 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) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(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) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(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 an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @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. + * + * @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(static_cast(*this), "Uninitialized delegate"); + return fn(instance, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + // no need to also test instance + 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. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance; + function_type *fn; +}; + +/** + * @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 +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> 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 &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // namespace entt + +#endif + +// #include "signal/dispatcher.hpp" +#ifndef ENTT_SIGNAL_DISPATCHER_HPP +#define ENTT_SIGNAL_DISPATCHER_HPP + +#include +#include +#include +#include +#include +#include +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 2 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @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 Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @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, "Invalid pointer type to non-static member object or function"); + + 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 Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #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 "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr; + size_type length; + hash_type hash; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers 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. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{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. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> 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 +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @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 differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @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 first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @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 first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @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 first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @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 first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) 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; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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 +[[nodiscard]] constexpr auto overload(Type Class::*member) 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 +[[nodiscard]] constexpr auto overload(Func *func) 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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include +#include +#include +#include +#include +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 { + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() noexcept + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) noexcept { + connect(function, payload); + } + + /** + * @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() noexcept { + instance = 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) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(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) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(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 an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @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. + * + * @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(static_cast(*this), "Uninitialized delegate"); + return fn(instance, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + // no need to also test instance + 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. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance; + function_type *fn; +}; + +/** + * @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 +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> 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 &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +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 Type A valid signal handler 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 Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh { + friend class sink>; + + using alloc_traits = std::allocator_traits; + using delegate_type = delegate; + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink>; + + /*! @brief Default constructor. */ + sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) + : calls{other.calls} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) + : calls{std::move(other.calls)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{std::move(other.calls), allocator} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ + sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { + calls = std::move(other.calls); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ + void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return calls.get_allocator(); + } + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const 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. + */ + [[nodiscard]] bool empty() const 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 pos = calls.size(); pos; --pos) { + calls[pos - 1u](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 pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + + if constexpr(std::is_invocable_r_v) { + if(func()) { + break; + } + } else { + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(calls[pos - 1u](args...))) { + break; + } + } else { + func(calls[pos - 1u](args...)); + } + } + } + } + +private: + container_type 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 { + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} {} + +public: + /*! @brief Default constructor. */ + connection() + : disconnect{}, + signal{} {} + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const 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 Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) noexcept + : conn{std::exchange(other.conn, {})} {} + + /*! @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 Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ + scoped_connection &operator=(scoped_connection &&other) noexcept { + conn = std::exchange(other.conn, {}); + return *this; + } + + /** + * @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. + */ + [[nodiscard]] explicit operator bool() const 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. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sink> { + using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; + using difference_type = typename signal_type::container_type::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(); + } + + template + void disconnect_if(Func callback) { + for(auto pos = signal->calls.size(); pos; --pos) { + if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { + elem = std::move(signal->calls.back()); + signal->calls.pop_back(); + } + } + } + +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) noexcept + : 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. + */ + [[nodiscard]] bool empty() const noexcept { + return signal->calls.empty(); + } + + /** + * @brief Connects a free function (with or without payload), a bound or an + * unbound member to a signal. + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&...value_or_instance) { + disconnect(value_or_instance...); + + delegate_type call{}; + call.template connect(value_or_instance...); + signal->calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance...); + return {std::move(conn), signal}; + } + + /** + * @brief Disconnects a free function (with or without payload), a bound or + * an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + */ + template + void disconnect(Type &&...value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance...); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @param value_or_instance A valid object that fits the purpose. + */ + void disconnect(const void *value_or_instance) { + if(value_or_instance) { + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); + } + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler 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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +sink(sigh &) -> sink>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct basic_dispatcher_handler { + virtual ~basic_dispatcher_handler() = default; + virtual void publish() = 0; + virtual void disconnect(void *) = 0; + virtual void clear() noexcept = 0; + virtual std::size_t size() const noexcept = 0; +}; + +template +class dispatcher_handler final: public basic_dispatcher_handler { + static_assert(std::is_same_v>, "Invalid type"); + + using alloc_traits = std::allocator_traits; + using signal_type = sigh; + using container_type = std::vector>; + +public: + using allocator_type = Allocator; + + dispatcher_handler(const allocator_type &allocator) + : signal{allocator}, + events{allocator} {} + + 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 disconnect(void *instance) override { + bucket().disconnect(instance); + } + + void clear() noexcept override { + events.clear(); + } + + [[nodiscard]] auto bucket() noexcept { + return typename signal_type::sink_type{signal}; + } + + void trigger(Type event) { + signal.publish(event); + } + + template + void enqueue(Args &&...args) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + events.push_back(Type{std::forward(args)...}); + } else { + events.emplace_back(std::forward(args)...); + } + } + + [[nodiscard]] std::size_t size() const noexcept override { + return events.size(); + } + +private: + signal_type signal; + container_type events; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @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 `Type`, listeners are such that they can be invoked with an argument of + * type `Type &`, 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. + * + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_dispatcher { + template + using handler_type = internal::dispatcher_handler; + + using key_type = id_type; + // std::shared_ptr because of its type erased allocator which is useful here + using mapped_type = std::shared_ptr; + + using alloc_traits = std::allocator_traits; + using container_allocator = typename alloc_traits::template rebind_alloc>; + using container_type = dense_map, container_allocator>; + + template + [[nodiscard]] handler_type &assure(const id_type id) { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + auto &&ptr = pools.first()[id]; + + if(!ptr) { + const auto &allocator = get_allocator(); + ptr = std::allocate_shared>(allocator, allocator); + } + + return static_cast &>(*ptr); + } + + template + [[nodiscard]] const handler_type *assure(const id_type id) const { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + + if(auto it = pools.first().find(id); it != pools.first().cend()) { + return static_cast *>(it->second.get()); + } + + return nullptr; + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + basic_dispatcher() + : basic_dispatcher{allocator_type{}} {} + + /** + * @brief Constructs a dispatcher with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_dispatcher(const allocator_type &allocator) + : pools{allocator, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_dispatcher(basic_dispatcher &&other) noexcept + : pools{std::move(other.pools)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept + : pools{container_type{std::move(other.pools.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed"); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This dispatcher. + */ + basic_dispatcher &operator=(basic_dispatcher &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed"); + pools = std::move(other.pools); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given dispatcher. + * @param other Dispatcher to exchange the content with. + */ + void swap(basic_dispatcher &other) { + using std::swap; + swap(pools, other.pools); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pools.second(); + } + + /** + * @brief Returns the number of pending events for a given type. + * @tparam Type Type of event for which to return the count. + * @param id Name used to map the event queue within the dispatcher. + * @return The number of pending events for the given type. + */ + template + size_type size(const id_type id = type_hash::value()) const noexcept { + const auto *cpool = assure>(id); + return cpool ? cpool->size() : 0u; + } + + /** + * @brief Returns the total number of pending events. + * @return The total number of pending events. + */ + size_type size() const noexcept { + size_type count{}; + + for(auto &&cpool: pools.first()) { + count += cpool.second->size(); + } + + return count; + } + + /** + * @brief Returns a sink object for the given event and queue. + * + * A sink is an opaque object used to connect listeners to events. + * + * The function type for a listener is _compatible_ with: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * The order of invocation of the listeners isn't guaranteed. + * + * @sa sink + * + * @tparam Type Type of event of which to get the sink. + * @param id Name used to map the event queue within the dispatcher. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto sink(const id_type id = type_hash::value()) { + return assure(id).bucket(); + } + + /** + * @brief Triggers an immediate event of a given type. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + */ + template + void trigger(Type &&value = {}) { + trigger(type_hash>::value(), std::forward(value)); + } + + /** + * @brief Triggers an immediate event on a queue of a given type. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + * @param id Name used to map the event queue within the dispatcher. + */ + template + void trigger(const id_type id, Type &&value = {}) { + assure>(id).trigger(std::forward(value)); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type 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) { + enqueue_hint(type_hash::value(), std::forward(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @param value An instance of the given type of event. + */ + template + void enqueue(Type &&value) { + enqueue_hint(type_hash>::value(), std::forward(value)); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param id Name used to map the event queue within the dispatcher. + * @param args Arguments to use to construct the event. + */ + template + void enqueue_hint(const id_type id, Args &&...args) { + assure(id).enqueue(std::forward(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @param id Name used to map the event queue within the dispatcher. + * @param value An instance of the given type of event. + */ + template + void enqueue_hint(const id_type id, Type &&value) { + assure>(id).enqueue(std::forward(value)); + } + + /** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @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 Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @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) { + for(auto &&cpool: pools.first()) { + cpool.second->disconnect(value_or_instance); + } + } + + /** + * @brief Discards all the events stored so far in a given queue. + * @tparam Type Type of event to discard. + * @param id Name used to map the event queue within the dispatcher. + */ + template + void clear(const id_type id = type_hash::value()) { + assure(id).clear(); + } + + /*! @brief Discards all the events queued so far. */ + void clear() noexcept { + for(auto &&cpool: pools.first()) { + cpool.second->clear(); + } + } + + /** + * @brief Delivers all the pending events of a given queue. + * @tparam Type Type of event to send. + * @param id Name used to map the event queue within the dispatcher. + */ + template + void update(const id_type id = type_hash::value()) { + assure(id).publish(); + } + + /*! @brief Delivers all the pending events. */ + void update() const { + for(auto &&cpool: pools.first()) { + cpool.second->publish(); + } + } + +private: + compressed_pair pools; +}; + +} // namespace entt + +#endif + +// #include "signal/emitter.hpp" +#ifndef ENTT_SIGNAL_EMITTER_HPP +#define ENTT_SIGNAL_EMITTER_HPP + +#include +#include +#include +// #include "../container/dense_map.hpp" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/utility.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief General purpose event emitter. + * + * To create an emitter type, derived classes must inherit from the base as: + * + * @code{.cpp} + * struct my_emitter: emitter { + * // ... + * } + * @endcode + * + * Handlers for the different events are created internally on the fly. It's not + * required to specify in advance the full list of accepted events.
+ * Moreover, whenever an event is published, an emitter also passes a reference + * to itself to its listeners. + * + * @tparam Derived Emitter type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class emitter { + using key_type = id_type; + using mapped_type = std::function; + + using alloc_traits = std::allocator_traits; + using container_allocator = typename alloc_traits::template rebind_alloc>; + using container_type = dense_map, container_allocator>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + emitter() + : emitter{allocator_type{}} {} + + /** + * @brief Constructs an emitter with a given allocator. + * @param allocator The allocator to use. + */ + explicit emitter(const allocator_type &allocator) + : handlers{allocator, allocator} {} + + /*! @brief Default destructor. */ + virtual ~emitter() noexcept { + static_assert(std::is_base_of_v, Derived>, "Invalid emitter type"); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + emitter(emitter &&other) noexcept + : handlers{std::move(other.handlers)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + emitter(emitter &&other, const allocator_type &allocator) noexcept + : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This dispatcher. + */ + emitter &operator=(emitter &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); + + handlers = std::move(other.handlers); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given emitter. + * @param other Emitter to exchange the content with. + */ + void swap(emitter &other) { + using std::swap; + swap(handlers, other.handlers); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return handlers.second(); + } + + /** + * @brief Publishes a given event. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + */ + template + void publish(Type &&value) { + if(const auto id = type_id().hash(); handlers.first().contains(id)) { + handlers.first()[id](&value); + } + } + + /** + * @brief Registers a listener with the event emitter. + * @tparam Type Type of event to which to connect the listener. + * @param func The listener to register. + */ + template + void on(std::function func) { + handlers.first().insert_or_assign(type_id().hash(), [func = std::move(func), this](void *value) { + func(*static_cast(value), static_cast(*this)); + }); + } + + /** + * @brief Disconnects a listener from the event emitter. + * @tparam Type Type of event of the listener. + */ + template + void erase() { + handlers.first().erase(type_hash>>::value()); + } + + /*! @brief Disconnects all the listeners. */ + void clear() noexcept { + handlers.first().clear(); + } + + /** + * @brief Checks if there are listeners registered for the specific event. + * @tparam Type Type of event to test. + * @return True if there are no listeners registered, false otherwise. + */ + template + [[nodiscard]] bool contains() const { + return handlers.first().contains(type_hash>>::value()); + } + + /** + * @brief Checks if there are listeners registered with the event emitter. + * @return True if there are no listeners registered, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return handlers.first().empty(); + } + +private: + compressed_pair handlers; +}; + +} // namespace entt + +#endif + +// #include "signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include +#include +#include +#include +#include +// #include "delegate.hpp" + +// #include "fwd.hpp" + + +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 Type A valid signal handler 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 Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh { + friend class sink>; + + using alloc_traits = std::allocator_traits; + using delegate_type = delegate; + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink>; + + /*! @brief Default constructor. */ + sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) + : calls{other.calls} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) + : calls{std::move(other.calls)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{std::move(other.calls), allocator} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ + sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { + calls = std::move(other.calls); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ + void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return calls.get_allocator(); + } + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const 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. + */ + [[nodiscard]] bool empty() const 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 pos = calls.size(); pos; --pos) { + calls[pos - 1u](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 pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + + if constexpr(std::is_invocable_r_v) { + if(func()) { + break; + } + } else { + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(calls[pos - 1u](args...))) { + break; + } + } else { + func(calls[pos - 1u](args...)); + } + } + } + } + +private: + container_type 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 { + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} {} + +public: + /*! @brief Default constructor. */ + connection() + : disconnect{}, + signal{} {} + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const 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 Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) noexcept + : conn{std::exchange(other.conn, {})} {} + + /*! @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 Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ + scoped_connection &operator=(scoped_connection &&other) noexcept { + conn = std::exchange(other.conn, {}); + return *this; + } + + /** + * @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. + */ + [[nodiscard]] explicit operator bool() const 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. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sink> { + using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; + using difference_type = typename signal_type::container_type::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(); + } + + template + void disconnect_if(Func callback) { + for(auto pos = signal->calls.size(); pos; --pos) { + if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { + elem = std::move(signal->calls.back()); + signal->calls.pop_back(); + } + } + } + +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) noexcept + : 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. + */ + [[nodiscard]] bool empty() const noexcept { + return signal->calls.empty(); + } + + /** + * @brief Connects a free function (with or without payload), a bound or an + * unbound member to a signal. + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&...value_or_instance) { + disconnect(value_or_instance...); + + delegate_type call{}; + call.template connect(value_or_instance...); + signal->calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance...); + return {std::move(conn), signal}; + } + + /** + * @brief Disconnects a free function (with or without payload), a bound or + * an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + */ + template + void disconnect(Type &&...value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance...); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @param value_or_instance A valid object that fits the purpose. + */ + void disconnect(const void *value_or_instance) { + if(value_or_instance) { + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); + } + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler 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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +sink(sigh &) -> sink>; + +} // namespace entt + +#endif + +// IWYU pragma: end_exports diff --git a/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.dll b/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.dll new file mode 100644 index 0000000..5c6a37d Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.dll differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.exp b/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.exp new file mode 100644 index 0000000..77abc69 Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.exp differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.lib b/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.lib new file mode 100644 index 0000000..7fa596d Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.lib differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.pdb b/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.pdb new file mode 100644 index 0000000..abbc659 Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Debug/DLL/Dynamic_runtime/openxr_loaderd.pdb differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.dll b/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.dll new file mode 100644 index 0000000..c6f41de Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.dll differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.exp b/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.exp new file mode 100644 index 0000000..77abc69 Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.exp differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.lib b/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.lib new file mode 100644 index 0000000..7fa596d Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.lib differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.pdb b/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.pdb new file mode 100644 index 0000000..26de980 Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Debug/DLL/Static_runtime/openxr_loaderd.pdb differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/Dynamic_runtime/openxr_loaderd.lib b/ThirdParty/openxr/Lib/x64/Debug/Dynamic_runtime/openxr_loaderd.lib new file mode 100644 index 0000000..4a2c177 Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Debug/Dynamic_runtime/openxr_loaderd.lib differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/Dynamic_runtime/openxr_loaderd.pdb b/ThirdParty/openxr/Lib/x64/Debug/Dynamic_runtime/openxr_loaderd.pdb new file mode 100644 index 0000000..dcc3d80 Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Debug/Dynamic_runtime/openxr_loaderd.pdb differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/openxr_loaderd.lib b/ThirdParty/openxr/Lib/x64/Debug/Static_runtime/openxr_loaderd.lib similarity index 72% rename from ThirdParty/openxr/Lib/x64/Debug/openxr_loaderd.lib rename to ThirdParty/openxr/Lib/x64/Debug/Static_runtime/openxr_loaderd.lib index d7c578e..2796aa5 100644 Binary files a/ThirdParty/openxr/Lib/x64/Debug/openxr_loaderd.lib and b/ThirdParty/openxr/Lib/x64/Debug/Static_runtime/openxr_loaderd.lib differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/Static_runtime/openxr_loaderd.pdb b/ThirdParty/openxr/Lib/x64/Debug/Static_runtime/openxr_loaderd.pdb new file mode 100644 index 0000000..49cfedf Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Debug/Static_runtime/openxr_loaderd.pdb differ diff --git a/ThirdParty/openxr/Lib/x64/Debug/openxr_loaderd.pdb b/ThirdParty/openxr/Lib/x64/Debug/openxr_loaderd.pdb deleted file mode 100644 index 26a0f34..0000000 Binary files a/ThirdParty/openxr/Lib/x64/Debug/openxr_loaderd.pdb and /dev/null differ diff --git a/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.dll b/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.dll new file mode 100644 index 0000000..c4f0024 Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.dll differ diff --git a/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.exp b/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.exp new file mode 100644 index 0000000..0d4934d Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.exp differ diff --git a/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.lib b/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.lib new file mode 100644 index 0000000..eebe861 Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Release/DLL/Dynamic_runtime/openxr_loader.lib differ diff --git a/ThirdParty/openxr/Lib/x64/Release/DLL/Static_runtime/openxr_loader.dll b/ThirdParty/openxr/Lib/x64/Release/DLL/Static_runtime/openxr_loader.dll new file mode 100644 index 0000000..b4f1df2 Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Release/DLL/Static_runtime/openxr_loader.dll differ diff --git a/ThirdParty/openxr/Lib/x64/Release/DLL/Static_runtime/openxr_loader.exp b/ThirdParty/openxr/Lib/x64/Release/DLL/Static_runtime/openxr_loader.exp new file mode 100644 index 0000000..0d4934d Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Release/DLL/Static_runtime/openxr_loader.exp differ diff --git a/ThirdParty/openxr/Lib/x64/Release/DLL/Static_runtime/openxr_loader.lib b/ThirdParty/openxr/Lib/x64/Release/DLL/Static_runtime/openxr_loader.lib new file mode 100644 index 0000000..eebe861 Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Release/DLL/Static_runtime/openxr_loader.lib differ diff --git a/ThirdParty/openxr/Lib/x64/Release/Dynamic_runtime/openxr_loader.lib b/ThirdParty/openxr/Lib/x64/Release/Dynamic_runtime/openxr_loader.lib new file mode 100644 index 0000000..4401bbd Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Release/Dynamic_runtime/openxr_loader.lib differ diff --git a/ThirdParty/openxr/Lib/x64/Release/Static_runtime/openxr_loader.lib b/ThirdParty/openxr/Lib/x64/Release/Static_runtime/openxr_loader.lib new file mode 100644 index 0000000..84eaf90 Binary files /dev/null and b/ThirdParty/openxr/Lib/x64/Release/Static_runtime/openxr_loader.lib differ diff --git a/ThirdParty/openxr/Lib/x64/Release/openxr_loader.lib b/ThirdParty/openxr/Lib/x64/Release/openxr_loader.lib deleted file mode 100644 index b64c136..0000000 Binary files a/ThirdParty/openxr/Lib/x64/Release/openxr_loader.lib and /dev/null differ diff --git a/ThirdParty/openxr/openxr.h b/ThirdParty/openxr/openxr.h index c70d272..9ac66d8 100644 --- a/ThirdParty/openxr/openxr.h +++ b/ThirdParty/openxr/openxr.h @@ -26,7 +26,7 @@ extern "C" { ((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL)) // OpenXR current version number. -#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 37) +#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 38) // OpenXR 1.0 version number #define XR_API_VERSION_1_0 XR_MAKE_VERSION(1, 0, XR_VERSION_PATCH(XR_CURRENT_API_VERSION)) @@ -773,6 +773,7 @@ typedef XrFlags64 XrCompositionLayerFlags; static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT = 0x00000001; static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT = 0x00000002; static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT = 0x00000004; +static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_INVERTED_ALPHA_BIT_EXT = 0x00000008; typedef XrFlags64 XrViewStateFlags; @@ -4173,7 +4174,7 @@ XR_DEFINE_HANDLE(XrPassthroughFB) XR_DEFINE_HANDLE(XrPassthroughLayerFB) XR_DEFINE_HANDLE(XrGeometryInstanceFB) #define XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB 256 -#define XR_FB_passthrough_SPEC_VERSION 3 +#define XR_FB_passthrough_SPEC_VERSION 4 #define XR_FB_PASSTHROUGH_EXTENSION_NAME "XR_FB_passthrough" typedef enum XrPassthroughLayerPurposeFB { @@ -4232,7 +4233,6 @@ typedef struct XrPassthroughLayerCreateInfoFB { XrPassthroughLayerPurposeFB purpose; } XrPassthroughLayerCreateInfoFB; -// XrCompositionLayerPassthroughFB extends XrCompositionLayerBaseHeader typedef struct XrCompositionLayerPassthroughFB { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -7157,7 +7157,7 @@ typedef struct XrHandTrackingDataSourceStateEXT { // XR_EXT_plane_detection is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_plane_detection 1 XR_DEFINE_HANDLE(XrPlaneDetectorEXT) -#define XR_EXT_plane_detection_SPEC_VERSION 1 +#define XR_EXT_plane_detection_SPEC_VERSION 2 #define XR_EXT_PLANE_DETECTION_EXTENSION_NAME "XR_EXT_plane_detection" typedef enum XrPlaneDetectorOrientationEXT { @@ -7445,6 +7445,12 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnableUserCalibrationEventsML( #define XR_YVR_controller_interaction_SPEC_VERSION 1 #define XR_YVR_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_YVR_controller_interaction" + +// XR_EXT_composition_layer_inverted_alpha is a preprocessor guard. Do not pass it to API calls. +#define XR_EXT_composition_layer_inverted_alpha 1 +#define XR_EXT_composition_layer_inverted_alpha_SPEC_VERSION 1 +#define XR_EXT_COMPOSITION_LAYER_INVERTED_ALPHA_EXTENSION_NAME "XR_EXT_composition_layer_inverted_alpha" + #ifdef __cplusplus } #endif diff --git a/ThirdParty/openxr/openxr_reflection.h b/ThirdParty/openxr/openxr_reflection.h index c1e9eb5..b9d4568 100644 --- a/ThirdParty/openxr/openxr_reflection.h +++ b/ThirdParty/openxr/openxr_reflection.h @@ -1451,6 +1451,7 @@ XR_ENUM_STR(XrResult); _(XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT, 0x00000001) \ _(XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT, 0x00000002) \ _(XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT, 0x00000004) \ + _(XR_COMPOSITION_LAYER_INVERTED_ALPHA_BIT_EXT, 0x00000008) \ #define XR_LIST_BITS_XrViewStateFlags(_) \ _(XR_VIEW_STATE_ORIENTATION_VALID_BIT, 0x00000001) \ @@ -5401,6 +5402,7 @@ XR_ENUM_STR(XrResult); _(XR_KHR_locate_spaces, 472) \ _(XR_ML_user_calibration, 473) \ _(XR_YVR_controller_interaction, 498) \ + _(XR_EXT_composition_layer_inverted_alpha, 555) \ _(XR_KHR_maintenance1, 711) \ diff --git a/ThirdParty/openxr/openxr_reflection_parent_structs.h b/ThirdParty/openxr/openxr_reflection_parent_structs.h index fd0354c..50466ab 100644 --- a/ThirdParty/openxr/openxr_reflection_parent_structs.h +++ b/ThirdParty/openxr/openxr_reflection_parent_structs.h @@ -32,6 +32,7 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a _avail(XrCompositionLayerCylinderKHR, XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR) \ _avail(XrCompositionLayerEquirectKHR, XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR) \ _avail(XrCompositionLayerEquirect2KHR, XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR) \ + _avail(XrCompositionLayerPassthroughFB, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB) \ _avail(XrCompositionLayerPassthroughHTC, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC) \ diff --git a/UpdateTimestamp.cmake b/UpdateTimestamp.cmake new file mode 100644 index 0000000..cc38903 --- /dev/null +++ b/UpdateTimestamp.cmake @@ -0,0 +1,2 @@ +string(TIMESTAMP ENGINE_BUILD_TIMESTAMP \"%Y%m%d%H%M%S\" UTC) +configure_file(${ENGINE_FOLDER}/ResourceManager/Timestamp.h.in ${ENGINE_FOLDER}/ResourceManager/Timestamp.h @ONLY) \ No newline at end of file