diff --git a/include/asset_base.hpp b/include/asset_base.hpp new file mode 100644 index 0000000..8b99dc6 --- /dev/null +++ b/include/asset_base.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +#include "type_info.hpp" + + +struct AssetGUID +{ + AssetGUID() = default; + AssetGUID(const char* data) : AssetGUID(std::string_view(data, 32)) {}; + AssetGUID(std::string_view data) + { + for (size_t i = 0; i < 16; ++i) Data[0] = (Data[0] << 4) | hexToVal(data[i]); + for (size_t i = 16; i < 32; ++i) Data[1] = (Data[1] << 4) | hexToVal(data[i]); + } + + static uint64_t hexToVal(char c) + { + return (c >= '0' && c <= '9') ? (c - '0') : (c - 'a' + 10); + } + + bool operator==(const AssetGUID& other) const + { + return Data[0] == other.Data[0] && Data[1] == other.Data[1]; + } + + static constexpr uint64_t DefaultVal = 0xe000000000000000; + + std::array Data{ 0, DefaultVal }; +}; + +struct AssetPath +{ + std::filesystem::path path{}; + AssetGUID GUID{}; +}; + +template +using unordered_map_guid = std::unordered_map; + +namespace std +{ + template <> + struct hash { + size_t operator()(const AssetGUID& v) const noexcept + { + auto h1 = std::hash{}(v.Data[0]); + auto h2 = std::hash{}(v.Data[1]); + return h1 ^ (h2 << 1); + } + }; +} + +inline AssetGUID ParseAssetGUID(const ryml::ConstNodeRef& node) +{ + auto guidStringSpan2 = node["guid"].val(); + + assert(guidStringSpan2.size() == 32); + + return AssetGUID{std::string_view(guidStringSpan2.data(), guidStringSpan2.size())}; +} + +struct AssetBase +{ + std::filesystem::path Path{}; + AssetGUID GUID{}; + + // static std::span GetExtensions(); +}; + +struct AssetCollectionBase +{ + std::vector*> AssetsInterface{}; + std::vector AssetNames{}; + + template + AssetType* GetAsset(AssetGUID guid) + { + for (size_t i = 0; i < AssetNames.size(); i++) + if (AssetNames[i] == type_name()) + { + auto& assetMap = (*AssetsInterface[i]); + auto it = assetMap.find(guid); + if (it != assetMap.end()) + { + return static_cast(it->second); + } + return nullptr; + } + return nullptr; + } +}; + +template > +struct AssetCollection : public AssetCollectionBase +{ + using AssetsT = typename AssetTypes::template AsContainedType; + + AssetsT Assets; + + AssetCollection(AssetsT&& assets) + : AssetNames(AssetTypes::get_names()) + , AssetsInterface(sizeof...(AssetTypes)) + , Assets(std::move(assets)) + { + size_t index = 0; + AssetTypes::ForEachT([this, &index]() + { + AssetsInterface[index++] = &std::get>(Assets); + }); + } +}; \ No newline at end of file diff --git a/include/assets/asset_base.hpp b/include/assets/asset_base.hpp deleted file mode 100644 index f1c95b1..0000000 --- a/include/assets/asset_base.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include - -struct AssetBase -{ -public: - int64_t ID; -}; - -struct AssetGUID -{ - AssetGUID() = default; - AssetGUID(const char* data) : AssetGUID(std::string_view(data, 32)) {}; - AssetGUID(std::string_view data) - { - for (size_t i = 0; i < 16; ++i) Data[0] = (Data[0] << 4) | hexToVal(data[i]); - for (size_t i = 16; i < 32; ++i) Data[1] = (Data[1] << 4) | hexToVal(data[i]); - } - - static uint64_t hexToVal(char c) - { - return (c >= '0' && c <= '9') ? (c - '0') : (c - 'a' + 10); - } - - bool operator==(const AssetGUID& other) const - { - return Data[0] == other.Data[0] && Data[1] == other.Data[1]; - } - - static constexpr uint64_t DefaultVal = 0xe000000000000000; - - std::array Data{ 0, DefaultVal }; -}; - - -namespace std -{ - template <> - struct hash { - size_t operator()(const AssetGUID& v) const noexcept - { - auto h1 = std::hash{}(v.Data[0]); - auto h2 = std::hash{}(v.Data[1]); - return h1 ^ (h2 << 1); - } - }; -} - -template -T* ParseAssetRef(const ryml::ConstNodeRef& node) -{ - T* asset; - node["fileID"] >> asset; - return asset; -} - -template -bool LinkAssetRef(const std::unordered_map& assetsMap, T*& asset) -{ - auto it = assetsMap.find(reinterpret_cast(asset)); - if (it != assetsMap.end()) - { - asset = reinterpret_cast(it->second); - return true; - } - asset = nullptr; - return false; -} - -inline AssetGUID ParseAssetGUID(const ryml::ConstNodeRef& node) -{ - auto guidStringSpan2 = node["guid"].val(); - - assert(guidStringSpan2.size() == 32); - - return AssetGUID{std::string_view(guidStringSpan2.data(), guidStringSpan2.size())}; -} \ No newline at end of file diff --git a/include/assets/custom_assets.hpp b/include/assets/custom_assets.hpp deleted file mode 100644 index 1596a4c..0000000 --- a/include/assets/custom_assets.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "asset_base.hpp" - -struct Drop -{ - int itemId; - int minAmount; - int maxAmount; - int dropChance; -}; - -struct Interactable_Resource : public AssetBase -{ - int maxHealth; - int respawnTime; - std::vector drops; - std::vector requiredTools; - int xp; - int typeId; -}; \ No newline at end of file diff --git a/include/assets/game_object.hpp b/include/assets/game_object.hpp deleted file mode 100644 index 2aa9fb4..0000000 --- a/include/assets/game_object.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include -#include - -#include - -#include "asset_base.hpp" -#include "transform.hpp" - -struct GameObjectAsset : public AssetBase -{ - std::string name; - uint8_t layer{ 0 }; - uint8_t namMeshLayer{ 0 }; - std::string tag; - - TransformAsset* transform{ nullptr }; - std::vector Components; -}; - -inline GameObjectAsset ParseGameObject(const ryml::ConstNodeRef& node) -{ - GameObjectAsset gameObject; - - node["m_Name"] >> gameObject.name; - node["m_Layer"] >> gameObject.layer; - node["m_TagString"] >> gameObject.tag; - - auto components = node["m_Component"]; - auto it = components.begin(); - - gameObject.transform = ParseAssetRef((*it)["component"]); - ++it; - for (; it != components.end(); ++it) - { - gameObject.Components.push_back(ParseAssetRef((*it)["component"])); - } - - return gameObject; -} - -inline void LinkGameObject(const std::unordered_map& assetsMap, GameObjectAsset& gameObject) -{ - LinkAssetRef(assetsMap, gameObject.transform); - for (int i = static_cast(gameObject.Components.size()) - 1; i >= 0; i--) - { - if (!LinkAssetRef(assetsMap, gameObject.Components[i])) - { - std::swap(gameObject.Components[i], gameObject.Components.back()); - gameObject.Components.pop_back(); - } - } -} \ No newline at end of file diff --git a/include/assets/image.h b/include/assets/image.h new file mode 100644 index 0000000..e69de29 diff --git a/include/assets/mesh.h b/include/assets/mesh.h new file mode 100644 index 0000000..e69de29 diff --git a/include/assets/mesh_filter.hpp b/include/assets/mesh_filter.hpp deleted file mode 100644 index 81ac966..0000000 --- a/include/assets/mesh_filter.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include "asset_base.hpp" - - -struct GameObjectAsset; - -struct MeshFilterAsset : AssetBase -{ - AssetGUID Mesh; - GameObjectAsset* GameObject{ nullptr }; -}; - -inline MeshFilterAsset ParseMeshFilter(const ryml::ConstNodeRef& node) -{ - MeshFilterAsset meshFilter; - - meshFilter.Mesh = ParseAssetGUID(node["m_Mesh"]); - meshFilter.GameObject = ParseAssetRef(node["m_GameObject"]); - - return meshFilter; -} - -inline void LinkMeshFilter(const std::unordered_map& assetsMap, MeshFilterAsset& meshFilter) -{ - LinkAssetRef(assetsMap, meshFilter.GameObject); -} \ No newline at end of file diff --git a/include/assets/prefab.h b/include/assets/prefab.h new file mode 100644 index 0000000..fdbe648 --- /dev/null +++ b/include/assets/prefab.h @@ -0,0 +1,29 @@ +#pragma once + +#include "asset_base.hpp" + +#include +#include +#include + +struct Prefab : public AssetBase +{ +public: + static constexpr std::array Extensions = { ".prefab" }; + +public: + std::unordered_map Data; + +public: + template + void Parse(ParsedProject& project, std::string& content) + { + using ObjectTypesT = typename ParsedProject::ObjectTypes; + + auto names = ObjectTypesT::get_names(); + ParseSceneYaml(content, false, names, [this](std::string_view componentName, int64_t ID, ryml::Tree&& tree) + { + Data.emplace(ID, std::move(tree)); + }); + } +}; \ No newline at end of file diff --git a/include/assets/prefab_instance.hpp b/include/assets/prefab_instance.hpp deleted file mode 100644 index 3819d75..0000000 --- a/include/assets/prefab_instance.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "asset_base.hpp" - -//struct PrefabInstanceAsset : AssetBase -//{ -// AssetGUID prefabGUID; -// ryml::Tree Data{}; -//}; - -struct PrefabAsset -{ - std::filesystem::path path; - std::string name; - - std::unordered_map Data; -}; - -//inline PrefabInstanceAsset ParsePrefabInstance(const ryml::ConstNodeRef& node) -//{ -// PrefabInstanceAsset prefabInstance; -// prefabInstance.prefabGUID = ParseAssetGUID(node["m_SourcePrefab"]); -// // prefabInstance.Data = node; -// return prefabInstance; -//} \ No newline at end of file diff --git a/include/assets/scene.h b/include/assets/scene.h new file mode 100644 index 0000000..42ce2ff --- /dev/null +++ b/include/assets/scene.h @@ -0,0 +1,118 @@ +#pragma once + +#include +#include +#include +#include + +#include "asset_base.hpp" +#include "type_base.hpp" + +struct Scene : public AssetBase +{ +public: + static constexpr std::array Extensions = { ".unity" }; + +private: + std::unordered_map IDtoAsset; + +public: + int64_t GetNewID(int64_t lastID = 0) + { + for (;; ++lastID) + { + if (IDtoAsset.find(lastID) == IDtoAsset.end()) + return lastID; + } + } + + template + void Parse(ParsedProject& project, std::string& content) + { + using ObjectTypesT = typename ParsedProject::ObjectTypes; + + auto& sceneData = project.CreateSceneData(GUID); + + // Create ParseContext for component parsing + ParseContext context(*this, project.Assets); + + auto names = ObjectTypesT::get_names(); + ParseSceneYaml(content, true, names, [&sceneData, &context](std::string_view componentName, int64_t ID, ryml::Tree&& tree) + { + sceneData.AddComponent(componentName, ID, std::move(tree), context); + }); + + // Link all components after parsing + LinkAssets(project); + } + + template + void LinkAssets(ParsedProject& project) + { + using ObjectTypesT = typename ParsedProject::ObjectTypes; + + auto& sceneData = project.GetSceneData(GUID); + + // Build the assets map (ID -> pointer) + std::unordered_map assetsMap; + ObjectTypesT::ForEachT([&]() -> void + { + auto& components = sceneData.template GetComponents(); + for (auto& component : components) + { + assetsMap.emplace(component.ID, &component); + } + }); + + // Link all components + ObjectTypesT::ForEachT([&]() -> void + { + auto& components = sceneData.template GetComponents(); + for (auto& component : components) + { + component.Link(assetsMap); + } + }); + } + + template + void MergeComponents(const ParsedProject& project, const Scene& otherScene) + { + using ObjectTypesT = typename ParsedProject::ObjectTypes; + + auto& thisSceneData = project.GetSceneData(GUID); + auto& otherSceneData = project.GetSceneData(otherScene.GUID); + + std::unordered_map newIDMap{}; + int64_t lastID = std::random_device{}(); + + ObjectTypesT::ForEachT([&]() -> void + { + auto& thisComponents = thisSceneData.template GetComponents(); + auto& otherComponents = otherSceneData.template GetComponents(); + + for (auto otherComponentCopy : otherComponents) + { + int64_t originalID = otherComponentCopy.ID; + lastID = GetNewID(lastID); + otherComponentCopy.ID = lastID; + + // register the new ID to the scene + newIDMap.emplace(originalID, lastID); + + // add the component to the scene + thisComponents.emplace_back(std::move(otherComponentCopy)); + } + }); + + auto& IDMapLinked = reinterpret_cast&>(newIDMap); + ObjectTypesT::ForEachT([&]() -> void + { + auto& thisComponents = thisSceneData.template GetComponents(); + for (auto& component : thisComponents) + { + component.Link(IDMapLinked); + } + }); + } +}; diff --git a/include/assets/scene.hpp b/include/assets/scene.hpp deleted file mode 100644 index 731cc39..0000000 --- a/include/assets/scene.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "game_object.hpp" -#include "transform.hpp" -#include "mesh_filter.hpp" -#include "prefab_instance.hpp" - -struct SceneAsset -{ -public: - std::filesystem::path path; - std::string name; - - std::vector gameObjects; - std::vector transforms; - std::vector meshFilters; - // std::vector prefabInstances; - - std::unordered_map IDtoAsset; - - -}; \ No newline at end of file diff --git a/include/assets/scripts.h b/include/assets/scripts.h new file mode 100644 index 0000000..23d8a16 --- /dev/null +++ b/include/assets/scripts.h @@ -0,0 +1,16 @@ +#pragma once + +#include "asset_base.hpp" + +struct Scripts : public AssetBase +{ +public: + static constexpr std::array Extensions = { ".cs" }; + +public: + template + void Parse(const ParsedProject& project, std::string& content) + { + + } +} \ No newline at end of file diff --git a/include/assets/transform.hpp b/include/assets/transform.hpp deleted file mode 100644 index 4bc43f3..0000000 --- a/include/assets/transform.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "asset_base.hpp" - -struct GameObjectAsset; - -struct TransformAsset : public AssetBase -{ - glm::quat Rotation; - glm::vec3 Position; - glm::vec3 Scale; - - glm::mat4 GlobalMatrix; - - GameObjectAsset* GameObject{ nullptr }; - std::vector Children; - TransformAsset* Parent{ nullptr }; -}; - -inline glm::vec3 ParseVector3(const ryml::ConstNodeRef& node) -{ - glm::vec3 vector; - node["x"] >> vector.x; - node["y"] >> vector.y; - node["z"] >> vector.z; - return vector; -} - -inline glm::quat ParseQuaternion(const ryml::ConstNodeRef& node) -{ - glm::quat quaternion; - node["x"] >> quaternion.x; - node["y"] >> quaternion.y; - node["z"] >> quaternion.z; - node["w"] >> quaternion.w; - return quaternion; -} - -inline TransformAsset ParseTransform(const ryml::ConstNodeRef& node) -{ - TransformAsset transform; - transform.Rotation = ParseQuaternion(node["m_LocalRotation"]); - transform.Position = ParseVector3(node["m_LocalPosition"]); - transform.Scale = ParseVector3(node["m_LocalScale"]); - - transform.Parent = ParseAssetRef(node["m_Father"]); - transform.GameObject = ParseAssetRef(node["m_GameObject"]); - - auto children = node["m_Children"]; - for (const auto& child : children) - { - transform.Children.push_back(ParseAssetRef(child)); - } - - return transform; -} - -inline void LinkTransform(const std::unordered_map& assetsMap, TransformAsset& transform) -{ - LinkAssetRef(assetsMap, transform.Parent); - for (int i = static_cast(transform.Children.size()) - 1; i >= 0; i--) - { - if (!LinkAssetRef(assetsMap, transform.Children[i])) - { - std::swap(transform.Children[i], transform.Children.back()); - transform.Children.pop_back(); - } - } - LinkAssetRef(assetsMap, transform.GameObject); -} \ No newline at end of file diff --git a/include/configs/achievements.h b/include/configs/achievements.h index dab7cd4..08769f5 100644 --- a/include/configs/achievements.h +++ b/include/configs/achievements.h @@ -34,8 +34,8 @@ struct Achievement }; // Achievements configuration functions -bool loadAchievementsFromXML(const std::string& filepath); -const Achievement* getAchievementById(uint16_t id); -const std::unordered_map& getAllAchievements(); +bool loadAchievementsFromXML(const std::string& filepath, std::unordered_map& achievements); +const Achievement* getAchievementById(uint16_t id, const std::unordered_map& achievements); +const std::unordered_map& getAllAchievements(const std::unordered_map& achievements); } // namespace cursebreaker \ No newline at end of file diff --git a/include/configs/harvestables.h b/include/configs/harvestables.h index a47e7e7..d3467d7 100644 --- a/include/configs/harvestables.h +++ b/include/configs/harvestables.h @@ -34,8 +34,8 @@ struct Harvestable }; // Harvestables configuration functions -bool loadHarvestablesFromXML(const std::string& filepath); -const Harvestable* getHarvestableById(uint16_t id); -const std::unordered_map& getAllHarvestables(); +bool loadHarvestablesFromXML(const std::string& filepath, std::unordered_map& harvestables); +const Harvestable* getHarvestableById(uint16_t id, const std::unordered_map& harvestables); +const std::unordered_map& getAllHarvestables(const std::unordered_map& harvestables); } // namespace cursebreaker \ No newline at end of file diff --git a/include/helpers.hpp b/include/configs/helpers.h similarity index 100% rename from include/helpers.hpp rename to include/configs/helpers.h diff --git a/include/configs/items.h b/include/configs/items.h index 88c7c85..68736f6 100644 --- a/include/configs/items.h +++ b/include/configs/items.h @@ -63,8 +63,8 @@ struct Item { }; // Items configuration functions -bool loadItemsFromXML(const std::string& filepath); -const Item* getItemById(uint16_t id); -const std::unordered_map& getAllItems(); +bool loadItemsFromXML(const std::string& filepath, std::unordered_map& items); +const Item* getItemById(uint16_t id, const std::unordered_map& items); +const std::unordered_map& getAllItems(const std::unordered_map& items); } // namespace cursebreaker diff --git a/include/configs/loot.h b/include/configs/loot.h index bdfdbe3..19ebb20 100644 --- a/include/configs/loot.h +++ b/include/configs/loot.h @@ -31,8 +31,8 @@ struct LootTable }; // Loot configuration functions -bool loadLootTablesFromXML(const std::string& filepath); -const LootTable* getLootTableById(uint16_t id); -const std::unordered_map& getAllLootTables(); +bool loadLootTablesFromXML(const std::string& filepath, std::unordered_map& lootTables); +const LootTable* getLootTableById(uint16_t id, const std::unordered_map& lootTables); +const std::unordered_map& getAllLootTables(const std::unordered_map& lootTables); } // namespace cursebreaker \ No newline at end of file diff --git a/include/configs/npcs.h b/include/configs/npcs.h index 3a9665c..c8a395c 100644 --- a/include/configs/npcs.h +++ b/include/configs/npcs.h @@ -36,8 +36,8 @@ struct NPC }; // NPCs configuration functions -bool loadNPCsFromXML(const std::string& filepath); -const NPC* getNPCById(uint16_t id); -const std::unordered_map& getAllNPCs(); +bool loadNPCsFromXML(const std::string& filepath, std::unordered_map& npcs); +const NPC* getNPCById(uint16_t id, const std::unordered_map& npcs); +const std::unordered_map& getAllNPCs(const std::unordered_map& npcs); } // namespace cursebreaker \ No newline at end of file diff --git a/include/configs/shops.h b/include/configs/shops.h index 6d09da9..a7c2d63 100644 --- a/include/configs/shops.h +++ b/include/configs/shops.h @@ -31,8 +31,8 @@ struct Shop }; // Shops configuration functions -bool loadShopsFromXML(const std::string& filepath); -const Shop* getShopById(uint16_t id); -const std::unordered_map& getAllShops(); +bool loadShopsFromXML(const std::string& filepath, std::unordered_map& shops); +const Shop* getShopById(uint16_t id, const std::unordered_map& shops); +const std::unordered_map& getAllShops(const std::unordered_map& shops); } // namespace cursebreaker \ No newline at end of file diff --git a/include/project_parser.h b/include/project_parser.h index 4a15ae8..c31e5ed 100644 --- a/include/project_parser.h +++ b/include/project_parser.h @@ -5,53 +5,295 @@ #include #include #include +#include +#include +#include +#include +#include #include #include +#include +#include -#include "assets/asset_base.hpp" -#include "assets/scene.hpp" +#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" -struct AssetPath -{ - std::filesystem::path path{}; - AssetGUID GUID{}; -}; - -namespace cursebreaker { - -struct ParsedProject +#include "type_info.hpp" +#include "asset_base.hpp" + +// Forward declarations for ForEachObj +struct GameObject; +struct Transform; + + + +class ParsedProjectBase { +public: std::filesystem::path m_projectRoot; - std::vector m_scriptFiles; - std::vector m_imageFiles; - std::vector m_meshFiles; - std::vector m_sceneFiles; - std::vector m_prefabFiles; +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(); } - std::vector m_prefabAssets; - std::vector m_sceneAssets; +protected: + std::vector m_assetNames; + std::vector m_monobehaviourNames; + std::vector m_unityNames; - std::unordered_map m_prefabsMap; - std::unordered_map m_scriptToClassHash; - - std::unordered_map m_items; - std::unordered_map m_lootTables; - std::unordered_map m_harvestables; - std::unordered_map m_npcs; - std::unordered_map m_shops; - std::unordered_map m_achievements; +public: + AssetCollectionBase* AssetsInterface; }; -void ParseProject(const std::filesystem::path& projectRoot); +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; -extern ParsedProject g_parsedProject; +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 = {}); -} // namespace cursebreaker \ No newline at end of file diff --git a/include/project_parser.hpp b/include/project_parser.hpp new file mode 100644 index 0000000..5b4b8f2 --- /dev/null +++ b/include/project_parser.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include "project_parser.h" + + + +template > +auto FindAssetFiles(std::string_view project_root) +{ + using AssetsContainer = typename AssetTypes::template AsContainedType; + AssetsContainer Assets{}; + + for (const auto& entry : std::filesystem::recursive_directory_iterator(project_root)) + { + if (entry.is_directory()) continue; + + std::string extension = entry.path().extension().string(); + + if (extension == std::string_view(".meta")) continue; + + AssetTypes::ForEachT([&Assets, &extension, &entry]() + { + constexpr auto assetExtensions = AssetT::Extensions; + + for (auto assetExtension : assetExtensions) + { + if (extension == assetExtension) + { + static_assert(std::is_base_of_v, "Asset Type must derive from AssetBase"); + + AssetT asset{}; + asset.Path = entry.path(); + asset.GUID = GetAssetGUIDFromFile(entry.path().string() + ".meta"); + std::get>(Assets).emplace(asset.GUID, std::move(asset)); + return; + } + } + }); + } + + return Assets; +} + +template , typename ProjectType> +void InitializeAssetFiles(ProjectType& project) +{ + AssetTypes::ForEachT([&project]() + { + std::string buffer{}; + + auto& assetMap = std::get>(project.Assets); + for (auto& [guid, asset] : assetMap) + { + buffer.clear(); + std::ifstream file(asset.Path); + buffer.assign(std::istreambuf_iterator(file), std::istreambuf_iterator()); + + asset.Parse(project, buffer); + } + }); +} + +template < + typename AssetTypes = TypeCollection<>, + typename UnityTypes = TypeCollection<>, + typename MonobehaviourTypes = TypeCollection<> +> +ParsedProject ParseProject(const std::filesystem::path& projectRoot) +{ + auto project = ParsedProject{projectRoot}; + + project.Assets = FindAssetFiles(projectRoot.generic_string()); + + InitializeAssetFiles(project); + + return project; +} \ No newline at end of file diff --git a/include/tree_builder.h b/include/tree_builder.h index 95923b9..7a31e40 100644 --- a/include/tree_builder.h +++ b/include/tree_builder.h @@ -1,5 +1,34 @@ -#pragma once - -#include "assets/scene.hpp" - -void BuildTree(SceneAsset& sceneAsset); \ No newline at end of file +#pragma once + +#include "assets/scene.h" +#include "types/transform.h" +#include + +void BuildTreeHelper(Transform& transform, const glm::mat4& parentMatrix); + +template +void BuildTree(ParsedProject& project, Scene& scene) +{ + // Get the scene data which contains the components + auto& sceneData = project.GetSceneData(scene.GUID); + + // Get all transforms from the scene data + auto& transforms = sceneData.template GetComponents(); + + // Find transforms with no parent (root transforms) + std::vector rootTransforms; + + for (auto& transform : transforms) + { + if (transform.Parent == nullptr) + { + rootTransforms.push_back(&transform); + } + } + + // Build tree for each root transform + for (auto rootTransform : rootTransforms) + { + BuildTreeHelper(*rootTransform, glm::mat4(1.0f)); + } +} \ No newline at end of file diff --git a/include/type_base.hpp b/include/type_base.hpp new file mode 100644 index 0000000..26f3d15 --- /dev/null +++ b/include/type_base.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "asset_base.hpp" + +// Forward declarations +struct Scene; + +struct TypeBase +{ +public: + int64_t ID; + int32_t TypeID{ -1 }; // Runtime type identification for bitset operations +}; + +struct ParseContext +{ + template + ParseContext(Scene& mainScene, AssetsT& assets) + : MainScene(mainScene) + , Assets(&assets) + {} + + Scene& MainScene; + AssetCollectionBase& Assets; +}; + +template +bool LinkObjectRef(const std::unordered_map& assetsMap, T*& asset) +{ + auto it = assetsMap.find(reinterpret_cast(asset)); + if (it != assetsMap.end()) + { + asset = reinterpret_cast(it->second); + return true; + } + asset = nullptr; + return false; +} + +template +T* ParseObjectRef(const ryml::ConstNodeRef& node) +{ + T* asset; + node["fileID"] >> asset; + return asset; +} \ No newline at end of file diff --git a/include/type_info.hpp b/include/type_info.hpp new file mode 100644 index 0000000..b98e4b5 --- /dev/null +++ b/include/type_info.hpp @@ -0,0 +1,107 @@ +#pragma once + + +/* +* Get the type name of a template parameter +*/ +template +consteval std::string_view type_name() { +#if defined(__clang__) || defined(__GNUC__) + constexpr std::string_view full = __PRETTY_FUNCTION__; + constexpr std::string_view prefix = "std::string_view type_name() [T = "; + constexpr std::string_view suffix = "]"; +#elif defined(_MSC_VER) + constexpr std::string_view full = __FUNCSIG__; + constexpr std::string_view prefix = "class std::basic_string_view > __cdecl type_name<"; + constexpr std::string_view suffix = ">(void)"; +#endif + + constexpr auto start = full.find(prefix) + prefix.size(); + constexpr auto end = full.rfind(suffix); + constexpr std::string_view raw_name = full.substr(start, end - start); + + // Remove "class " or "struct " prefix + constexpr std::string_view class_prefix = "class "; + constexpr std::string_view struct_prefix = "struct "; + + constexpr std::string_view without_keyword = + raw_name.starts_with(class_prefix) ? raw_name.substr(class_prefix.size()) : + raw_name.starts_with(struct_prefix) ? raw_name.substr(struct_prefix.size()) : + raw_name; + + // Remove namespace - keep only the part after the last "::" + constexpr auto last_colon = without_keyword.rfind("::"); + constexpr std::string_view final_name = + (last_colon != std::string_view::npos) + ? without_keyword.substr(last_colon + 2) + : without_keyword; + + return final_name; +} + +template +struct TypeCollection +{ + using Tuple = std::tuple; + + template + using Merge = TypeCollection; + + template + struct MergeWithImpl; + + template + struct MergeWithImpl> + { + using type = TypeCollection; + }; + + template + using MergeWith = typename MergeWithImpl::type; + + template