#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "type_base.hpp" #include "assets/scene.h" #include "configs/items.h" #include "configs/loot.h" #include "configs/harvestables.h" #include "configs/npcs.h" #include "configs/shops.h" #include "configs/achievements.h" #include "types/prefab_instance.h" #include "type_info.hpp" #include "asset_base.hpp" // Forward declarations for ForEachObj struct GameObject; struct Transform; class ParsedProjectBase { public: std::filesystem::path m_projectRoot; public: ParsedProjectBase(const std::filesystem::path& projectRoot) : m_projectRoot{ projectRoot } {} bool HasAssetType(std::string_view name) { return std::find(m_assetNames.begin(), m_assetNames.end(), name) != m_assetNames.end(); } bool HasMonobehaviourType(std::string_view name) { return std::find(m_monobehaviourNames.begin(), m_monobehaviourNames.end(), name) != m_monobehaviourNames.end(); } bool HasUnityType(std::string_view name) { return std::find(m_unityNames.begin(), m_unityNames.end(), name) != m_unityNames.end(); } protected: std::vector m_assetNames; std::vector m_monobehaviourNames; std::vector m_unityNames; public: AssetCollectionBase* AssetsInterface; }; template < typename UnityTypes = TypeCollection<>, typename MonobehaviourTypes = TypeCollection<> > class SceneData final { public: using UnityTypesT = UnityTypes; using MonobehaviourTypesT = MonobehaviourTypes; using AllTypes = typename UnityTypes::template MergeWith; using AllTypesContainer = typename AllTypes::template AsContainedType; private: AllTypesContainer m_Data{}; public: void AddComponent(std::string_view ComponentName, int64_t ID, ryml::Tree&& tree, ParseContext& context) { auto it = AddObjectFunctions.find(ComponentName); assert(it != AddObjectFunctions.end()); auto nodeRef = tree.rootref(); it->second(static_cast(&m_Data), static_cast(&nodeRef), ID, static_cast(&context)); } template const std::vector& GetComponents() const { return std::get>(m_Data); } template std::vector& GetComponents() { return std::get>(m_Data); } /** * Iterate over all GameObjects that have ALL of the specified component types. * * This function uses a bitset-based component mask for efficient filtering, * only checking components on GameObjects that have all required types. * * Usage: * sceneData.ForEachObj([](GameObject& go, Transform& t) { ... }); * sceneData.ForEachObj([](GameObject& go, Transform& t, MeshFilter& m) { ... }); * * @tparam ComponentTypes The component types to filter by * @param func Lambda/function taking GameObject reference followed by references to the specified component types */ template void ForEachObj(Func&& func) { ForEachObjImpl(*this, std::forward(func)); } /** * Iterate over all GameObjects that have ALL of the specified component types (const version). * * @tparam ComponentTypes The component types to filter by * @param func Lambda/function taking const GameObject reference followed by const references to the specified component types */ template void ForEachObj(Func&& func) const { ForEachObjImpl(*this, std::forward(func)); } private: // Helper implementation for ForEachObj that works with both const and non-const template static void ForEachObjImpl(SceneDataRef&& sceneData, Func&& func) { static_assert(sizeof...(ComponentTypes) > 0, "At least one component type must be specified"); // Build the required component mask std::bitset<64> requiredMask; ((requiredMask.set(AllTypes::template GetTypeIndex())), ...); // Get the GameObjects vector (const or non-const depending on sceneData) auto& gameObjects = sceneData.template GetComponents(); // Iterate through all game objects for (auto& gameObject : gameObjects) { // Check if this GameObject has all required components if ((gameObject.ComponentMask & requiredMask) == requiredMask) { // Use conditional const for pointer types based on whether sceneData is const using GameObjectT = std::remove_reference_t; using TransformPtrT = std::conditional_t, const Transform*, Transform*>; // Extract the components std::tuple, const ComponentTypes*, ComponentTypes*>...> components; bool allFound = true; // Try to extract each required component type ([&]() { using ComponentPtrT = std::conditional_t, const ComponentTypes*, ComponentTypes*>; if constexpr (std::is_same_v) { std::get(components) = gameObject.transform; if (!gameObject.transform) allFound = false; } else { ComponentPtrT found = nullptr; for (auto* comp : gameObject.Components) { if (comp && comp->TypeID == AllTypes::template GetTypeIndex()) { found = static_cast(comp); break; } } std::get(components) = found; if (!found) allFound = false; } }(), ...); // If all components were found, call the function with GameObject first, then components if (allFound) { std::apply([&](auto*... comps) { func(gameObject, *comps...); }, components); } } } } using AddObjectFunctionT = void*(*)(void*, const void*, int64_t, void*); static auto GetAddObjectFunctions() { std::unordered_map functions; AllTypes::ForEachT([&functions]() { functions[type_name()] = +[](void* pSceneData, const void* pComponentYaml, int64_t ID, void* pContext) -> void* { SceneData& sceneData = *static_cast(pSceneData); const ryml::ConstNodeRef& componentYaml = *static_cast(pComponentYaml); ParseContext& context = *static_cast(pContext); T component{}; component.ID = ID; component.TypeID = AllTypes::template GetTypeIndex(); // Set the TypeID for bitset operations // Parse the component with context component.Parse(componentYaml.child(0).child(0), context); return &std::get>(sceneData.m_Data).emplace_back(std::move(component)); }; }); return functions; } private: inline static std::unordered_map AddObjectFunctions = GetAddObjectFunctions(); }; template < typename UnityTypes = TypeCollection<>, typename MonobehaviourTypes = TypeCollection<>, typename AssetTypes = TypeCollection<> > class ParsedProject final : public ParsedProjectBase { public: using UnityTypesT = UnityTypes; using MonobehaviourTypesT = MonobehaviourTypes; using AssetTypesT = AssetTypes; using ObjectTypes = typename UnityTypes::template MergeWith; using AllTypes = typename UnityTypes::template MergeWith; public: AssetCollection Assets; private: std::unordered_map> m_sceneData; public: ParsedProject(const std::filesystem::path& projectRoot) : ParsedProjectBase(projectRoot) { auto asset_names = AssetTypes::get_names(); m_assetNames.assign(asset_names.begin(), asset_names.end()); auto monobehaviour_names = MonobehaviourTypes::get_names(); m_monobehaviourNames.assign(monobehaviour_names.begin(), monobehaviour_names.end()); auto unity_names = UnityTypes::get_names(); m_unityNames.assign(unity_names.begin(), unity_names.end()); Assets = AssetCollectionBase{ asset_names }; AssetsInterface = &Assets; } auto& CreateSceneData(AssetGUID guid) { return m_sceneData[guid]; } auto& GetSceneData(AssetGUID guid) { auto it = m_sceneData.find(guid); assert(it != m_sceneData.end()); return it->second; } auto& GetSceneData(AssetGUID guid) const { auto it = m_sceneData.find(guid); assert(it != m_sceneData.end()); return it->second; } template std::unordered_map& GetAssets() { return std::get>(Assets); } template const std::unordered_map& GetAssets() const { return std::get>(Assets); } template void ForEachAsset(Function func) { AssetTypes::ForEachT([&]() { func(GetAssets()); }); } }; AssetGUID GetAssetGUIDFromFile(const std::filesystem::path& path); void ParseSceneYaml(std::string& content, bool in_place, std::span assetNames = {}, std::function callback = {});