imgration 14/12/2025
This commit is contained in:
@@ -5,53 +5,295 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <unordered_map>
|
||||
#include <string_view>
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <bitset>
|
||||
#include <type_traits>
|
||||
|
||||
#include <SQLiteCpp/SQLiteCpp.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <ryml.hpp>
|
||||
#include <ryml_std.hpp>
|
||||
|
||||
#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<AssetPath> m_scriptFiles;
|
||||
std::vector<AssetPath> m_imageFiles;
|
||||
std::vector<AssetPath> m_meshFiles;
|
||||
std::vector<AssetPath> m_sceneFiles;
|
||||
std::vector<AssetPath> 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<PrefabAsset> m_prefabAssets;
|
||||
std::vector<SceneAsset> m_sceneAssets;
|
||||
protected:
|
||||
std::vector<std::string_view> m_assetNames;
|
||||
std::vector<std::string_view> m_monobehaviourNames;
|
||||
std::vector<std::string_view> m_unityNames;
|
||||
|
||||
std::unordered_map<AssetGUID, PrefabAsset*> m_prefabsMap;
|
||||
std::unordered_map<AssetGUID, uint32_t> m_scriptToClassHash;
|
||||
|
||||
std::unordered_map<uint16_t, Item> m_items;
|
||||
std::unordered_map<uint16_t, LootTable> m_lootTables;
|
||||
std::unordered_map<uint16_t, Harvestable> m_harvestables;
|
||||
std::unordered_map<uint16_t, NPC> m_npcs;
|
||||
std::unordered_map<uint16_t, Shop> m_shops;
|
||||
std::unordered_map<uint16_t, Achievement> 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<MonobehaviourTypes>;
|
||||
using AllTypesContainer = typename AllTypes::template AsContainedType<std::vector>;
|
||||
|
||||
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<void*>(&m_Data), static_cast<const void*>(&nodeRef), ID, static_cast<void*>(&context));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const std::vector<T>& GetComponents() const
|
||||
{
|
||||
return std::get<std::vector<T>>(m_Data);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T>& GetComponents()
|
||||
{
|
||||
return std::get<std::vector<T>>(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<Transform>([](GameObject& go, Transform& t) { ... });
|
||||
* sceneData.ForEachObj<Transform, MeshFilter>([](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 <typename... ComponentTypes, typename Func>
|
||||
void ForEachObj(Func&& func)
|
||||
{
|
||||
ForEachObjImpl<ComponentTypes...>(*this, std::forward<Func>(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 <typename... ComponentTypes, typename Func>
|
||||
void ForEachObj(Func&& func) const
|
||||
{
|
||||
ForEachObjImpl<ComponentTypes...>(*this, std::forward<Func>(func));
|
||||
}
|
||||
|
||||
private:
|
||||
// Helper implementation for ForEachObj that works with both const and non-const
|
||||
template <typename... ComponentTypes, typename SceneDataRef, typename Func>
|
||||
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<ComponentTypes>())), ...);
|
||||
|
||||
// Get the GameObjects vector (const or non-const depending on sceneData)
|
||||
auto& gameObjects = sceneData.template GetComponents<GameObject>();
|
||||
|
||||
// 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<decltype(gameObject)>;
|
||||
using TransformPtrT = std::conditional_t<std::is_const_v<GameObjectT>,
|
||||
const Transform*, Transform*>;
|
||||
|
||||
// Extract the components
|
||||
std::tuple<std::conditional_t<std::is_const_v<GameObjectT>,
|
||||
const ComponentTypes*, ComponentTypes*>...> components;
|
||||
bool allFound = true;
|
||||
|
||||
// Try to extract each required component type
|
||||
([&]()
|
||||
{
|
||||
using ComponentPtrT = std::conditional_t<std::is_const_v<GameObjectT>,
|
||||
const ComponentTypes*, ComponentTypes*>;
|
||||
|
||||
if constexpr (std::is_same_v<ComponentTypes, Transform>)
|
||||
{
|
||||
std::get<ComponentPtrT>(components) = gameObject.transform;
|
||||
if (!gameObject.transform) allFound = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ComponentPtrT found = nullptr;
|
||||
for (auto* comp : gameObject.Components)
|
||||
{
|
||||
if (comp && comp->TypeID == AllTypes::template GetTypeIndex<ComponentTypes>())
|
||||
{
|
||||
found = static_cast<ComponentPtrT>(comp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::get<ComponentPtrT>(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<std::string_view, AddObjectFunctionT> functions;
|
||||
AllTypes::ForEachT([&functions]<typename T>()
|
||||
{
|
||||
functions[type_name<T>()] = +[](void* pSceneData, const void* pComponentYaml, int64_t ID, void* pContext) -> void*
|
||||
{
|
||||
SceneData& sceneData = *static_cast<SceneData*>(pSceneData);
|
||||
const ryml::ConstNodeRef& componentYaml = *static_cast<const ryml::ConstNodeRef*>(pComponentYaml);
|
||||
ParseContext& context = *static_cast<ParseContext*>(pContext);
|
||||
|
||||
T component{};
|
||||
component.ID = ID;
|
||||
component.TypeID = AllTypes::template GetTypeIndex<T>(); // Set the TypeID for bitset operations
|
||||
|
||||
// Parse the component with context
|
||||
component.Parse(componentYaml.child(0).child(0), context);
|
||||
|
||||
return &std::get<std::vector<T>>(sceneData.m_Data).emplace_back(std::move(component));
|
||||
};
|
||||
});
|
||||
return functions;
|
||||
}
|
||||
|
||||
private:
|
||||
inline static std::unordered_map<std::string_view, AddObjectFunctionT> 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<MonobehaviourTypes>;
|
||||
using AllTypes = typename UnityTypes::template MergeWith<MonobehaviourTypes>;
|
||||
|
||||
public:
|
||||
AssetCollection<AssetTypes> Assets;
|
||||
|
||||
private:
|
||||
std::unordered_map<AssetGUID, SceneData<UnityTypes, MonobehaviourTypes>> 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 <typename Asset>
|
||||
std::unordered_map<AssetGUID, Asset>& GetAssets() { return std::get<std::unordered_map<AssetGUID, Asset>>(Assets); }
|
||||
template <typename Asset>
|
||||
const std::unordered_map<AssetGUID, Asset>& GetAssets() const { return std::get<std::unordered_map<AssetGUID, Asset>>(Assets); }
|
||||
|
||||
template <typename Function>
|
||||
void ForEachAsset(Function func)
|
||||
{
|
||||
AssetTypes::ForEachT([&]<typename T>() {
|
||||
func(GetAssets<T>());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
AssetGUID GetAssetGUIDFromFile(const std::filesystem::path& path);
|
||||
void ParseSceneYaml(std::string& content, bool in_place, std::span<std::string_view> assetNames = {}, std::function<void(std::string_view, int64_t, ryml::Tree&&)> callback = {});
|
||||
|
||||
} // namespace cursebreaker
|
||||
Reference in New Issue
Block a user