imgration 14/12/2025
This commit is contained in:
120
include/asset_base.hpp
Normal file
120
include/asset_base.hpp
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <array>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <ryml.hpp>
|
||||||
|
|
||||||
|
#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<uint64_t, 2> Data{ 0, DefaultVal };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AssetPath
|
||||||
|
{
|
||||||
|
std::filesystem::path path{};
|
||||||
|
AssetGUID GUID{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using unordered_map_guid = std::unordered_map<AssetGUID, T>;
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct hash<AssetGUID> {
|
||||||
|
size_t operator()(const AssetGUID& v) const noexcept
|
||||||
|
{
|
||||||
|
auto h1 = std::hash<uint64_t>{}(v.Data[0]);
|
||||||
|
auto h2 = std::hash<uint64_t>{}(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<std::string_view> GetExtensions();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AssetCollectionBase
|
||||||
|
{
|
||||||
|
std::vector<std::unordered_map<AssetGUID, AssetBase*>*> AssetsInterface{};
|
||||||
|
std::vector<std::string_view> AssetNames{};
|
||||||
|
|
||||||
|
template <typename AssetType>
|
||||||
|
AssetType* GetAsset(AssetGUID guid)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < AssetNames.size(); i++)
|
||||||
|
if (AssetNames[i] == type_name<AssetType>())
|
||||||
|
{
|
||||||
|
auto& assetMap = (*AssetsInterface[i]);
|
||||||
|
auto it = assetMap.find(guid);
|
||||||
|
if (it != assetMap.end())
|
||||||
|
{
|
||||||
|
return static_cast<AssetType*>(it->second);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AssetTypes = TypeCollection<>>
|
||||||
|
struct AssetCollection : public AssetCollectionBase
|
||||||
|
{
|
||||||
|
using AssetsT = typename AssetTypes::template AsContainedType<unordered_map_guid>;
|
||||||
|
|
||||||
|
AssetsT Assets;
|
||||||
|
|
||||||
|
AssetCollection(AssetsT&& assets)
|
||||||
|
: AssetNames(AssetTypes::get_names())
|
||||||
|
, AssetsInterface(sizeof...(AssetTypes))
|
||||||
|
, Assets(std::move(assets))
|
||||||
|
{
|
||||||
|
size_t index = 0;
|
||||||
|
AssetTypes::ForEachT([this, &index]<typename AssetT>()
|
||||||
|
{
|
||||||
|
AssetsInterface[index++] = &std::get<std::unordered_map<AssetGUID, AssetT>>(Assets);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <array>
|
|
||||||
#include <string_view>
|
|
||||||
#include <span>
|
|
||||||
|
|
||||||
#include <ryml.hpp>
|
|
||||||
#include <ryml_std.hpp>
|
|
||||||
#include <c4/format.hpp>
|
|
||||||
|
|
||||||
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<uint64_t, 2> Data{ 0, DefaultVal };
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
namespace std
|
|
||||||
{
|
|
||||||
template <>
|
|
||||||
struct hash<AssetGUID> {
|
|
||||||
size_t operator()(const AssetGUID& v) const noexcept
|
|
||||||
{
|
|
||||||
auto h1 = std::hash<uint64_t>{}(v.Data[0]);
|
|
||||||
auto h2 = std::hash<uint64_t>{}(v.Data[1]);
|
|
||||||
return h1 ^ (h2 << 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T* ParseAssetRef(const ryml::ConstNodeRef& node)
|
|
||||||
{
|
|
||||||
T* asset;
|
|
||||||
node["fileID"] >> asset;
|
|
||||||
return asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool LinkAssetRef(const std::unordered_map<int64_t, AssetBase*>& assetsMap, T*& asset)
|
|
||||||
{
|
|
||||||
auto it = assetsMap.find(reinterpret_cast<int64_t>(asset));
|
|
||||||
if (it != assetsMap.end())
|
|
||||||
{
|
|
||||||
asset = reinterpret_cast<T*>(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())};
|
|
||||||
}
|
|
||||||
@@ -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<Drop> drops;
|
|
||||||
std::vector<int> requiredTools;
|
|
||||||
int xp;
|
|
||||||
int typeId;
|
|
||||||
};
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <ryml_std.hpp>
|
|
||||||
|
|
||||||
#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<AssetBase*> 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<TransformAsset>((*it)["component"]);
|
|
||||||
++it;
|
|
||||||
for (; it != components.end(); ++it)
|
|
||||||
{
|
|
||||||
gameObject.Components.push_back(ParseAssetRef<AssetBase>((*it)["component"]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return gameObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void LinkGameObject(const std::unordered_map<int64_t, AssetBase*>& assetsMap, GameObjectAsset& gameObject)
|
|
||||||
{
|
|
||||||
LinkAssetRef<TransformAsset>(assetsMap, gameObject.transform);
|
|
||||||
for (int i = static_cast<int>(gameObject.Components.size()) - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (!LinkAssetRef<AssetBase>(assetsMap, gameObject.Components[i]))
|
|
||||||
{
|
|
||||||
std::swap(gameObject.Components[i], gameObject.Components.back());
|
|
||||||
gameObject.Components.pop_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0
include/assets/image.h
Normal file
0
include/assets/image.h
Normal file
0
include/assets/mesh.h
Normal file
0
include/assets/mesh.h
Normal file
@@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#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<GameObjectAsset>(node["m_GameObject"]);
|
|
||||||
|
|
||||||
return meshFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void LinkMeshFilter(const std::unordered_map<int64_t, AssetBase*>& assetsMap, MeshFilterAsset& meshFilter)
|
|
||||||
{
|
|
||||||
LinkAssetRef<GameObjectAsset>(assetsMap, meshFilter.GameObject);
|
|
||||||
}
|
|
||||||
29
include/assets/prefab.h
Normal file
29
include/assets/prefab.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "asset_base.hpp"
|
||||||
|
|
||||||
|
#include <ryml_std.hpp>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
struct Prefab : public AssetBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr std::array<std::string_view, 1> Extensions = { ".prefab" };
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::unordered_map<int64_t, ryml::Tree> Data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename ParsedProject>
|
||||||
|
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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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<int64_t, ryml::Tree> Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
//inline PrefabInstanceAsset ParsePrefabInstance(const ryml::ConstNodeRef& node)
|
|
||||||
//{
|
|
||||||
// PrefabInstanceAsset prefabInstance;
|
|
||||||
// prefabInstance.prefabGUID = ParseAssetGUID(node["m_SourcePrefab"]);
|
|
||||||
// // prefabInstance.Data = node;
|
|
||||||
// return prefabInstance;
|
|
||||||
//}
|
|
||||||
118
include/assets/scene.h
Normal file
118
include/assets/scene.h
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#include "asset_base.hpp"
|
||||||
|
#include "type_base.hpp"
|
||||||
|
|
||||||
|
struct Scene : public AssetBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr std::array<std::string_view, 1> Extensions = { ".unity" };
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<int64_t, void*> IDtoAsset;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int64_t GetNewID(int64_t lastID = 0)
|
||||||
|
{
|
||||||
|
for (;; ++lastID)
|
||||||
|
{
|
||||||
|
if (IDtoAsset.find(lastID) == IDtoAsset.end())
|
||||||
|
return lastID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ParsedProject>
|
||||||
|
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 <typename ParsedProject>
|
||||||
|
void LinkAssets(ParsedProject& project)
|
||||||
|
{
|
||||||
|
using ObjectTypesT = typename ParsedProject::ObjectTypes;
|
||||||
|
|
||||||
|
auto& sceneData = project.GetSceneData(GUID);
|
||||||
|
|
||||||
|
// Build the assets map (ID -> pointer)
|
||||||
|
std::unordered_map<int64_t, TypeBase*> assetsMap;
|
||||||
|
ObjectTypesT::ForEachT([&]<typename T>() -> void
|
||||||
|
{
|
||||||
|
auto& components = sceneData.template GetComponents<T>();
|
||||||
|
for (auto& component : components)
|
||||||
|
{
|
||||||
|
assetsMap.emplace(component.ID, &component);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Link all components
|
||||||
|
ObjectTypesT::ForEachT([&]<typename T>() -> void
|
||||||
|
{
|
||||||
|
auto& components = sceneData.template GetComponents<T>();
|
||||||
|
for (auto& component : components)
|
||||||
|
{
|
||||||
|
component.Link(assetsMap);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ParsedProject>
|
||||||
|
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<int64_t, int64_t> newIDMap{};
|
||||||
|
int64_t lastID = std::random_device{}();
|
||||||
|
|
||||||
|
ObjectTypesT::ForEachT([&]<typename T>() -> void
|
||||||
|
{
|
||||||
|
auto& thisComponents = thisSceneData.template GetComponents<T>();
|
||||||
|
auto& otherComponents = otherSceneData.template GetComponents<T>();
|
||||||
|
|
||||||
|
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<std::unordered_map<int64_t, TypeBase*>&>(newIDMap);
|
||||||
|
ObjectTypesT::ForEachT([&]<typename T>() -> void
|
||||||
|
{
|
||||||
|
auto& thisComponents = thisSceneData.template GetComponents<T>();
|
||||||
|
for (auto& component : thisComponents)
|
||||||
|
{
|
||||||
|
component.Link(IDMapLinked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#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<GameObjectAsset> gameObjects;
|
|
||||||
std::vector<TransformAsset> transforms;
|
|
||||||
std::vector<MeshFilterAsset> meshFilters;
|
|
||||||
// std::vector<PrefabInstanceAsset> prefabInstances;
|
|
||||||
|
|
||||||
std::unordered_map<int64_t, void*> IDtoAsset;
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
16
include/assets/scripts.h
Normal file
16
include/assets/scripts.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "asset_base.hpp"
|
||||||
|
|
||||||
|
struct Scripts : public AssetBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr std::array<std::string_view, 1> Extensions = { ".cs" };
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename ParsedProject>
|
||||||
|
void Parse(const ParsedProject& project, std::string& content)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
|
||||||
|
|
||||||
#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<TransformAsset*> 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<TransformAsset>(node["m_Father"]);
|
|
||||||
transform.GameObject = ParseAssetRef<GameObjectAsset>(node["m_GameObject"]);
|
|
||||||
|
|
||||||
auto children = node["m_Children"];
|
|
||||||
for (const auto& child : children)
|
|
||||||
{
|
|
||||||
transform.Children.push_back(ParseAssetRef<TransformAsset>(child));
|
|
||||||
}
|
|
||||||
|
|
||||||
return transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void LinkTransform(const std::unordered_map<int64_t, AssetBase*>& assetsMap, TransformAsset& transform)
|
|
||||||
{
|
|
||||||
LinkAssetRef<TransformAsset>(assetsMap, transform.Parent);
|
|
||||||
for (int i = static_cast<int>(transform.Children.size()) - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (!LinkAssetRef<TransformAsset>(assetsMap, transform.Children[i]))
|
|
||||||
{
|
|
||||||
std::swap(transform.Children[i], transform.Children.back());
|
|
||||||
transform.Children.pop_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LinkAssetRef<GameObjectAsset>(assetsMap, transform.GameObject);
|
|
||||||
}
|
|
||||||
@@ -34,8 +34,8 @@ struct Achievement
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Achievements configuration functions
|
// Achievements configuration functions
|
||||||
bool loadAchievementsFromXML(const std::string& filepath);
|
bool loadAchievementsFromXML(const std::string& filepath, std::unordered_map<uint16_t, Achievement>& achievements);
|
||||||
const Achievement* getAchievementById(uint16_t id);
|
const Achievement* getAchievementById(uint16_t id, const std::unordered_map<uint16_t, Achievement>& achievements);
|
||||||
const std::unordered_map<uint16_t, Achievement>& getAllAchievements();
|
const std::unordered_map<uint16_t, Achievement>& getAllAchievements(const std::unordered_map<uint16_t, Achievement>& achievements);
|
||||||
|
|
||||||
} // namespace cursebreaker
|
} // namespace cursebreaker
|
||||||
@@ -34,8 +34,8 @@ struct Harvestable
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Harvestables configuration functions
|
// Harvestables configuration functions
|
||||||
bool loadHarvestablesFromXML(const std::string& filepath);
|
bool loadHarvestablesFromXML(const std::string& filepath, std::unordered_map<uint16_t, Harvestable>& harvestables);
|
||||||
const Harvestable* getHarvestableById(uint16_t id);
|
const Harvestable* getHarvestableById(uint16_t id, const std::unordered_map<uint16_t, Harvestable>& harvestables);
|
||||||
const std::unordered_map<uint16_t, Harvestable>& getAllHarvestables();
|
const std::unordered_map<uint16_t, Harvestable>& getAllHarvestables(const std::unordered_map<uint16_t, Harvestable>& harvestables);
|
||||||
|
|
||||||
} // namespace cursebreaker
|
} // namespace cursebreaker
|
||||||
@@ -63,8 +63,8 @@ struct Item {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Items configuration functions
|
// Items configuration functions
|
||||||
bool loadItemsFromXML(const std::string& filepath);
|
bool loadItemsFromXML(const std::string& filepath, std::unordered_map<uint16_t, Item>& items);
|
||||||
const Item* getItemById(uint16_t id);
|
const Item* getItemById(uint16_t id, const std::unordered_map<uint16_t, Item>& items);
|
||||||
const std::unordered_map<uint16_t, Item>& getAllItems();
|
const std::unordered_map<uint16_t, Item>& getAllItems(const std::unordered_map<uint16_t, Item>& items);
|
||||||
|
|
||||||
} // namespace cursebreaker
|
} // namespace cursebreaker
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ struct LootTable
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Loot configuration functions
|
// Loot configuration functions
|
||||||
bool loadLootTablesFromXML(const std::string& filepath);
|
bool loadLootTablesFromXML(const std::string& filepath, std::unordered_map<uint16_t, LootTable>& lootTables);
|
||||||
const LootTable* getLootTableById(uint16_t id);
|
const LootTable* getLootTableById(uint16_t id, const std::unordered_map<uint16_t, LootTable>& lootTables);
|
||||||
const std::unordered_map<uint16_t, LootTable>& getAllLootTables();
|
const std::unordered_map<uint16_t, LootTable>& getAllLootTables(const std::unordered_map<uint16_t, LootTable>& lootTables);
|
||||||
|
|
||||||
} // namespace cursebreaker
|
} // namespace cursebreaker
|
||||||
@@ -36,8 +36,8 @@ struct NPC
|
|||||||
};
|
};
|
||||||
|
|
||||||
// NPCs configuration functions
|
// NPCs configuration functions
|
||||||
bool loadNPCsFromXML(const std::string& filepath);
|
bool loadNPCsFromXML(const std::string& filepath, std::unordered_map<uint16_t, NPC>& npcs);
|
||||||
const NPC* getNPCById(uint16_t id);
|
const NPC* getNPCById(uint16_t id, const std::unordered_map<uint16_t, NPC>& npcs);
|
||||||
const std::unordered_map<uint16_t, NPC>& getAllNPCs();
|
const std::unordered_map<uint16_t, NPC>& getAllNPCs(const std::unordered_map<uint16_t, NPC>& npcs);
|
||||||
|
|
||||||
} // namespace cursebreaker
|
} // namespace cursebreaker
|
||||||
@@ -31,8 +31,8 @@ struct Shop
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Shops configuration functions
|
// Shops configuration functions
|
||||||
bool loadShopsFromXML(const std::string& filepath);
|
bool loadShopsFromXML(const std::string& filepath, std::unordered_map<uint16_t, Shop>& shops);
|
||||||
const Shop* getShopById(uint16_t id);
|
const Shop* getShopById(uint16_t id, const std::unordered_map<uint16_t, Shop>& shops);
|
||||||
const std::unordered_map<uint16_t, Shop>& getAllShops();
|
const std::unordered_map<uint16_t, Shop>& getAllShops(const std::unordered_map<uint16_t, Shop>& shops);
|
||||||
|
|
||||||
} // namespace cursebreaker
|
} // namespace cursebreaker
|
||||||
@@ -5,53 +5,295 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <string_view>
|
||||||
|
#include <functional>
|
||||||
|
#include <span>
|
||||||
|
#include <bitset>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include <SQLiteCpp/SQLiteCpp.h>
|
#include <SQLiteCpp/SQLiteCpp.h>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <ryml.hpp>
|
||||||
|
#include <ryml_std.hpp>
|
||||||
|
|
||||||
#include "assets/asset_base.hpp"
|
#include "type_base.hpp"
|
||||||
#include "assets/scene.hpp"
|
#include "assets/scene.h"
|
||||||
#include "configs/items.h"
|
#include "configs/items.h"
|
||||||
#include "configs/loot.h"
|
#include "configs/loot.h"
|
||||||
#include "configs/harvestables.h"
|
#include "configs/harvestables.h"
|
||||||
#include "configs/npcs.h"
|
#include "configs/npcs.h"
|
||||||
#include "configs/shops.h"
|
#include "configs/shops.h"
|
||||||
#include "configs/achievements.h"
|
#include "configs/achievements.h"
|
||||||
|
#include "types/prefab_instance.h"
|
||||||
|
|
||||||
struct AssetPath
|
#include "type_info.hpp"
|
||||||
{
|
#include "asset_base.hpp"
|
||||||
std::filesystem::path path{};
|
|
||||||
AssetGUID GUID{};
|
// Forward declarations for ForEachObj
|
||||||
};
|
struct GameObject;
|
||||||
|
struct Transform;
|
||||||
namespace cursebreaker {
|
|
||||||
|
|
||||||
struct ParsedProject
|
|
||||||
|
class ParsedProjectBase
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
std::filesystem::path m_projectRoot;
|
std::filesystem::path m_projectRoot;
|
||||||
|
|
||||||
std::vector<AssetPath> m_scriptFiles;
|
public:
|
||||||
std::vector<AssetPath> m_imageFiles;
|
ParsedProjectBase(const std::filesystem::path& projectRoot) : m_projectRoot{ projectRoot } {}
|
||||||
std::vector<AssetPath> m_meshFiles;
|
|
||||||
std::vector<AssetPath> m_sceneFiles;
|
bool HasAssetType(std::string_view name) { return std::find(m_assetNames.begin(), m_assetNames.end(), name) != m_assetNames.end(); }
|
||||||
std::vector<AssetPath> m_prefabFiles;
|
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;
|
protected:
|
||||||
std::vector<SceneAsset> m_sceneAssets;
|
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;
|
public:
|
||||||
std::unordered_map<AssetGUID, uint32_t> m_scriptToClassHash;
|
AssetCollectionBase* AssetsInterface;
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
|
||||||
77
include/project_parser.hpp
Normal file
77
include/project_parser.hpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "project_parser.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <typename AssetTypes = TypeCollection<>>
|
||||||
|
auto FindAssetFiles(std::string_view project_root)
|
||||||
|
{
|
||||||
|
using AssetsContainer = typename AssetTypes::template AsContainedType<unordered_map_guid>;
|
||||||
|
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]<typename AssetT>()
|
||||||
|
{
|
||||||
|
constexpr auto assetExtensions = AssetT::Extensions;
|
||||||
|
|
||||||
|
for (auto assetExtension : assetExtensions)
|
||||||
|
{
|
||||||
|
if (extension == assetExtension)
|
||||||
|
{
|
||||||
|
static_assert(std::is_base_of_v<AssetBase, AssetT>, "Asset Type must derive from AssetBase");
|
||||||
|
|
||||||
|
AssetT asset{};
|
||||||
|
asset.Path = entry.path();
|
||||||
|
asset.GUID = GetAssetGUIDFromFile(entry.path().string() + ".meta");
|
||||||
|
std::get<std::unordered_map<AssetGUID, AssetT>>(Assets).emplace(asset.GUID, std::move(asset));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Assets;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AssetTypes = TypeCollection<>, typename ProjectType>
|
||||||
|
void InitializeAssetFiles(ProjectType& project)
|
||||||
|
{
|
||||||
|
AssetTypes::ForEachT([&project]<typename AssetT>()
|
||||||
|
{
|
||||||
|
std::string buffer{};
|
||||||
|
|
||||||
|
auto& assetMap = std::get<std::unordered_map<AssetGUID, AssetT>>(project.Assets);
|
||||||
|
for (auto& [guid, asset] : assetMap)
|
||||||
|
{
|
||||||
|
buffer.clear();
|
||||||
|
std::ifstream file(asset.Path);
|
||||||
|
buffer.assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
|
asset.Parse(project, buffer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename AssetTypes = TypeCollection<>,
|
||||||
|
typename UnityTypes = TypeCollection<>,
|
||||||
|
typename MonobehaviourTypes = TypeCollection<>
|
||||||
|
>
|
||||||
|
ParsedProject<UnityTypes, MonobehaviourTypes, AssetTypes> ParseProject(const std::filesystem::path& projectRoot)
|
||||||
|
{
|
||||||
|
auto project = ParsedProject<UnityTypes, MonobehaviourTypes, AssetTypes>{projectRoot};
|
||||||
|
|
||||||
|
project.Assets = FindAssetFiles<AssetTypes>(projectRoot.generic_string());
|
||||||
|
|
||||||
|
InitializeAssetFiles<AssetTypes>(project);
|
||||||
|
|
||||||
|
return project;
|
||||||
|
}
|
||||||
@@ -1,5 +1,34 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "assets/scene.hpp"
|
#include "assets/scene.h"
|
||||||
|
#include "types/transform.h"
|
||||||
void BuildTree(SceneAsset& sceneAsset);
|
#include <vector>
|
||||||
|
|
||||||
|
void BuildTreeHelper(Transform& transform, const glm::mat4& parentMatrix);
|
||||||
|
|
||||||
|
template <typename ParsedProject>
|
||||||
|
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<Transform>();
|
||||||
|
|
||||||
|
// Find transforms with no parent (root transforms)
|
||||||
|
std::vector<Transform*> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
56
include/type_base.hpp
Normal file
56
include/type_base.hpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <array>
|
||||||
|
#include <string_view>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#include <ryml.hpp>
|
||||||
|
#include <ryml_std.hpp>
|
||||||
|
#include <c4/format.hpp>
|
||||||
|
|
||||||
|
#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 <typename AssetsT>
|
||||||
|
ParseContext(Scene& mainScene, AssetsT& assets)
|
||||||
|
: MainScene(mainScene)
|
||||||
|
, Assets(&assets)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Scene& MainScene;
|
||||||
|
AssetCollectionBase& Assets;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool LinkObjectRef(const std::unordered_map<int64_t, TypeBase*>& assetsMap, T*& asset)
|
||||||
|
{
|
||||||
|
auto it = assetsMap.find(reinterpret_cast<int64_t>(asset));
|
||||||
|
if (it != assetsMap.end())
|
||||||
|
{
|
||||||
|
asset = reinterpret_cast<T*>(it->second);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
asset = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* ParseObjectRef(const ryml::ConstNodeRef& node)
|
||||||
|
{
|
||||||
|
T* asset;
|
||||||
|
node["fileID"] >> asset;
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
107
include/type_info.hpp
Normal file
107
include/type_info.hpp
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the type name of a template parameter
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
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<char,struct std::char_traits<char> > __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 <typename ... Ts>
|
||||||
|
struct TypeCollection
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Ts...>;
|
||||||
|
|
||||||
|
template <typename ... OtherTs>
|
||||||
|
using Merge = TypeCollection<Ts..., OtherTs...>;
|
||||||
|
|
||||||
|
template <typename OtherTypeCollection>
|
||||||
|
struct MergeWithImpl;
|
||||||
|
|
||||||
|
template <typename ... OtherTs>
|
||||||
|
struct MergeWithImpl<TypeCollection<OtherTs...>>
|
||||||
|
{
|
||||||
|
using type = TypeCollection<Ts..., OtherTs...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OtherTypeCollection>
|
||||||
|
using MergeWith = typename MergeWithImpl<OtherTypeCollection>::type;
|
||||||
|
|
||||||
|
template <template <typename> typename ContainerType>
|
||||||
|
using AsContainedType = std::tuple<ContainerType<Ts>...>;
|
||||||
|
|
||||||
|
static constexpr std::array<std::string_view, sizeof...(Ts)> get_names()
|
||||||
|
{
|
||||||
|
return std::array<std::string_view, sizeof...(Ts)>{ type_name<Ts>()... };
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr bool has_duplicate_names()
|
||||||
|
{
|
||||||
|
constexpr auto names = get_names();
|
||||||
|
for (size_t i = 0; i < names.size(); i++)
|
||||||
|
for (size_t j = i + 1; j < names.size(); j++)
|
||||||
|
if (names[i] == names[j])
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for duplicates
|
||||||
|
static_assert(!has_duplicate_names());
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
static constexpr void ForEachT(Func&& func)
|
||||||
|
{
|
||||||
|
(func.template operator()<Ts>(), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static constexpr bool HasT()
|
||||||
|
{
|
||||||
|
return (std::is_same_v<T, Ts> || ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the index of a type in the collection
|
||||||
|
template <typename T>
|
||||||
|
static constexpr int32_t GetTypeIndex()
|
||||||
|
{
|
||||||
|
int32_t index = 0;
|
||||||
|
bool found = false;
|
||||||
|
((std::is_same_v<T, Ts> ? (found = true, false) : (found ? false : (++index, false))), ...);
|
||||||
|
return found ? index : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the type index at runtime by comparing type IDs
|
||||||
|
static constexpr size_t Count() { return sizeof...(Ts); }
|
||||||
|
};
|
||||||
11
include/types/Interactable_resource.h
Normal file
11
include/types/Interactable_resource.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "type_base.hpp"
|
||||||
|
|
||||||
|
struct Interactable_Resource : public TypeBase
|
||||||
|
{
|
||||||
|
uint16_t ResourceID;
|
||||||
|
|
||||||
|
void Parse(const ryml::ConstNodeRef& node, ParseContext& context);
|
||||||
|
void Link(const std::unordered_map<int64_t, TypeBase*>& assetsMap) {};
|
||||||
|
};
|
||||||
65
include/types/game_object.h
Normal file
65
include/types/game_object.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
|
#include <ryml_std.hpp>
|
||||||
|
|
||||||
|
#include "type_base.hpp"
|
||||||
|
#include "transform.h"
|
||||||
|
|
||||||
|
struct GameObject : public TypeBase
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
uint8_t layer{ 0 };
|
||||||
|
uint8_t namMeshLayer{ 0 };
|
||||||
|
std::string tag;
|
||||||
|
|
||||||
|
Transform* transform{ nullptr };
|
||||||
|
std::vector<TypeBase*> Components;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ComponentMask is a bitset where each bit corresponds to a component type index.
|
||||||
|
* This allows for extremely fast checking of whether a GameObject has specific components.
|
||||||
|
* The bit is set during the Link() phase when components are resolved.
|
||||||
|
* Max 64 different component types are supported.
|
||||||
|
*/
|
||||||
|
std::bitset<64> ComponentMask;
|
||||||
|
|
||||||
|
void Parse(const ryml::ConstNodeRef& node, ParseContext& context);
|
||||||
|
void Link(const std::unordered_map<int64_t, TypeBase*>& assetsMap);
|
||||||
|
|
||||||
|
// Check if this GameObject has a specific component type
|
||||||
|
template <typename T>
|
||||||
|
bool HasComponent() const
|
||||||
|
{
|
||||||
|
// Type index must be set by the system
|
||||||
|
for (auto* comp : Components)
|
||||||
|
{
|
||||||
|
if (comp && comp->TypeID >= 0 && comp->TypeID < 64)
|
||||||
|
{
|
||||||
|
if (ComponentMask.test(comp->TypeID))
|
||||||
|
{
|
||||||
|
// Additional runtime check could be added here
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a component of a specific type
|
||||||
|
template <typename T>
|
||||||
|
T* GetComponent()
|
||||||
|
{
|
||||||
|
for (auto* comp : Components)
|
||||||
|
{
|
||||||
|
if (auto* typedComp = dynamic_cast<T*>(comp))
|
||||||
|
{
|
||||||
|
return typedComp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
18
include/types/mesh_filter.h
Normal file
18
include/types/mesh_filter.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "type_base.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
struct GameObject;
|
||||||
|
|
||||||
|
struct MeshFilter : TypeBase
|
||||||
|
{
|
||||||
|
AssetGUID Mesh;
|
||||||
|
GameObject* GameObject{ nullptr };
|
||||||
|
|
||||||
|
void Parse(const ryml::ConstNodeRef& node, ParseContext& context);
|
||||||
|
void Link(const std::unordered_map<int64_t, TypeBase*>& assetsMap);
|
||||||
|
};
|
||||||
|
|
||||||
13
include/types/prefab_instance.h
Normal file
13
include/types/prefab_instance.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "type_base.hpp"
|
||||||
|
|
||||||
|
struct PrefabInstance : public TypeBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Parse(const ryml::ConstNodeRef& node, ParseContext& context);
|
||||||
|
void Link(const std::unordered_map<int64_t, TypeBase*>& assetsMap) {};
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::unordered_map<int64_t, ryml::Tree> Data;
|
||||||
|
};
|
||||||
27
include/types/transform.h
Normal file
27
include/types/transform.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
#include "type_base.hpp"
|
||||||
|
|
||||||
|
struct GameObject;
|
||||||
|
|
||||||
|
struct Transform : public TypeBase
|
||||||
|
{
|
||||||
|
glm::quat Rotation;
|
||||||
|
glm::vec3 Position;
|
||||||
|
glm::vec3 Scale;
|
||||||
|
|
||||||
|
glm::mat4 GlobalMatrix;
|
||||||
|
|
||||||
|
GameObject* GameObject{ nullptr };
|
||||||
|
std::vector<Transform*> Children;
|
||||||
|
Transform* Parent{ nullptr };
|
||||||
|
|
||||||
|
void Parse(const ryml::ConstNodeRef& node, ParseContext& context);
|
||||||
|
void Link(const std::unordered_map<int64_t, TypeBase*>& assetsMap);
|
||||||
|
};
|
||||||
|
|
||||||
3
src/assets/prefab.cpp
Normal file
3
src/assets/prefab.cpp
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#include "assets/prefab.h"
|
||||||
|
#include "project_parser.h"
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "configs/achievements.h"
|
#include "configs/achievements.h"
|
||||||
#include "project_parser.h"
|
#include "project_parser.h"
|
||||||
#include "helpers.hpp"
|
#include "configs/helpers.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace cursebreaker {
|
namespace cursebreaker {
|
||||||
@@ -9,10 +9,10 @@ namespace cursebreaker {
|
|||||||
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
||||||
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
||||||
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
||||||
static void parseAchievement(tinyxml2::XMLElement* achievementElement);
|
static void parseAchievement(tinyxml2::XMLElement* achievementElement, std::unordered_map<uint16_t, Achievement>& achievements);
|
||||||
static void parseRequirements(tinyxml2::XMLElement* achievementElement, Achievement& achievement);
|
static void parseRequirements(tinyxml2::XMLElement* achievementElement, Achievement& achievement);
|
||||||
|
|
||||||
bool loadAchievementsFromXML(const std::string& filepath) {
|
bool loadAchievementsFromXML(const std::string& filepath, std::unordered_map<uint16_t, Achievement>& achievements) {
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
||||||
|
|
||||||
@@ -27,20 +27,20 @@ bool loadAchievementsFromXML(const std::string& filepath) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_parsedProject.m_achievements.clear();
|
achievements.clear();
|
||||||
|
|
||||||
// Parse all achievements
|
// Parse all achievements
|
||||||
for (tinyxml2::XMLElement* achievementElement = root->FirstChildElement("achievement");
|
for (tinyxml2::XMLElement* achievementElement = root->FirstChildElement("achievement");
|
||||||
achievementElement != nullptr;
|
achievementElement != nullptr;
|
||||||
achievementElement = achievementElement->NextSiblingElement("achievement")) {
|
achievementElement = achievementElement->NextSiblingElement("achievement")) {
|
||||||
parseAchievement(achievementElement);
|
parseAchievement(achievementElement, achievements);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Loaded " << g_parsedProject.m_achievements.size() << " achievements from XML" << std::endl;
|
std::cout << "Loaded " << achievements.size() << " achievements from XML" << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseAchievement(tinyxml2::XMLElement* achievementElement) {
|
void parseAchievement(tinyxml2::XMLElement* achievementElement, std::unordered_map<uint16_t, Achievement>& achievements) {
|
||||||
Achievement achievement;
|
Achievement achievement;
|
||||||
|
|
||||||
// Parse basic attributes
|
// Parse basic attributes
|
||||||
@@ -56,7 +56,7 @@ void parseAchievement(tinyxml2::XMLElement* achievementElement) {
|
|||||||
// Parse child elements
|
// Parse child elements
|
||||||
parseRequirements(achievementElement, achievement);
|
parseRequirements(achievementElement, achievement);
|
||||||
|
|
||||||
g_parsedProject.m_achievements[achievement.id] = std::move(achievement);
|
achievements[achievement.id] = std::move(achievement);
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseRequirements(tinyxml2::XMLElement* achievementElement, Achievement& achievement) {
|
void parseRequirements(tinyxml2::XMLElement* achievementElement, Achievement& achievement) {
|
||||||
@@ -74,13 +74,13 @@ void parseRequirements(tinyxml2::XMLElement* achievementElement, Achievement& ac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Achievement* getAchievementById(uint16_t id) {
|
const Achievement* getAchievementById(uint16_t id, const std::unordered_map<uint16_t, Achievement>& achievements) {
|
||||||
auto it = g_parsedProject.m_achievements.find(id);
|
auto it = achievements.find(id);
|
||||||
return (it != g_parsedProject.m_achievements.end()) ? &it->second : nullptr;
|
return (it != achievements.end()) ? &it->second : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::unordered_map<uint16_t, Achievement>& getAllAchievements() {
|
const std::unordered_map<uint16_t, Achievement>& getAllAchievements(const std::unordered_map<uint16_t, Achievement>& achievements) {
|
||||||
return g_parsedProject.m_achievements;
|
return achievements;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "configs/harvestables.h"
|
#include "configs/harvestables.h"
|
||||||
#include "project_parser.h"
|
#include "project_parser.h"
|
||||||
#include "helpers.hpp"
|
#include "configs/helpers.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -11,10 +11,10 @@ namespace cursebreaker {
|
|||||||
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
||||||
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
||||||
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
||||||
static void parseHarvestable(tinyxml2::XMLElement* harvestableElement);
|
static void parseHarvestable(tinyxml2::XMLElement* harvestableElement, std::unordered_map<uint16_t, Harvestable>& harvestables);
|
||||||
static void parseLoot(tinyxml2::XMLElement* harvestableElement, Harvestable& harvestable);
|
static void parseLoot(tinyxml2::XMLElement* harvestableElement, Harvestable& harvestable);
|
||||||
|
|
||||||
bool loadHarvestablesFromXML(const std::string& filepath) {
|
bool loadHarvestablesFromXML(const std::string& filepath, std::unordered_map<uint16_t, Harvestable>& harvestables) {
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
||||||
|
|
||||||
@@ -29,20 +29,20 @@ bool loadHarvestablesFromXML(const std::string& filepath) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_parsedProject.m_harvestables.clear();
|
harvestables.clear();
|
||||||
|
|
||||||
// Parse all harvestables
|
// Parse all harvestables
|
||||||
for (tinyxml2::XMLElement* harvestableElement = root->FirstChildElement("harvestable");
|
for (tinyxml2::XMLElement* harvestableElement = root->FirstChildElement("harvestable");
|
||||||
harvestableElement != nullptr;
|
harvestableElement != nullptr;
|
||||||
harvestableElement = harvestableElement->NextSiblingElement("harvestable")) {
|
harvestableElement = harvestableElement->NextSiblingElement("harvestable")) {
|
||||||
parseHarvestable(harvestableElement);
|
parseHarvestable(harvestableElement, harvestables);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Loaded " << g_parsedProject.m_harvestables.size() << " harvestables from XML" << std::endl;
|
std::cout << "Loaded " << harvestables.size() << " harvestables from XML" << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseHarvestable(tinyxml2::XMLElement* harvestableElement) {
|
void parseHarvestable(tinyxml2::XMLElement* harvestableElement, std::unordered_map<uint16_t, Harvestable>& harvestables) {
|
||||||
Harvestable harvestable;
|
Harvestable harvestable;
|
||||||
|
|
||||||
// Parse basic attributes
|
// Parse basic attributes
|
||||||
@@ -58,7 +58,7 @@ void parseHarvestable(tinyxml2::XMLElement* harvestableElement) {
|
|||||||
// Parse child elements
|
// Parse child elements
|
||||||
parseLoot(harvestableElement, harvestable);
|
parseLoot(harvestableElement, harvestable);
|
||||||
|
|
||||||
g_parsedProject.m_harvestables[harvestable.id] = std::move(harvestable);
|
harvestables[harvestable.id] = std::move(harvestable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseLoot(tinyxml2::XMLElement* harvestableElement, Harvestable& harvestable) {
|
void parseLoot(tinyxml2::XMLElement* harvestableElement, Harvestable& harvestable) {
|
||||||
@@ -75,13 +75,13 @@ void parseLoot(tinyxml2::XMLElement* harvestableElement, Harvestable& harvestabl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Harvestable* getHarvestableById(uint16_t id) {
|
const Harvestable* getHarvestableById(uint16_t id, const std::unordered_map<uint16_t, Harvestable>& harvestables) {
|
||||||
auto it = g_parsedProject.m_harvestables.find(id);
|
auto it = harvestables.find(id);
|
||||||
return (it != g_parsedProject.m_harvestables.end()) ? &it->second : nullptr;
|
return (it != harvestables.end()) ? &it->second : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::unordered_map<uint16_t, Harvestable>& getAllHarvestables() {
|
const std::unordered_map<uint16_t, Harvestable>& getAllHarvestables(const std::unordered_map<uint16_t, Harvestable>& harvestables) {
|
||||||
return g_parsedProject.m_harvestables;
|
return harvestables;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "helpers.hpp"
|
#include "configs/helpers.h"
|
||||||
|
|
||||||
namespace cursebreaker {
|
namespace cursebreaker {
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "configs/items.h"
|
#include "configs/items.h"
|
||||||
#include "project_parser.h"
|
#include "project_parser.h"
|
||||||
#include "helpers.hpp"
|
#include "configs/helpers.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -12,11 +12,11 @@ namespace cursebreaker {
|
|||||||
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
||||||
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
||||||
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
||||||
static void parseItem(tinyxml2::XMLElement* itemElement);
|
static void parseItem(tinyxml2::XMLElement* itemElement, std::unordered_map<uint16_t, Item>& items);
|
||||||
static void parseStats(tinyxml2::XMLElement* itemElement, Item& item);
|
static void parseStats(tinyxml2::XMLElement* itemElement, Item& item);
|
||||||
static void parseCraftings(tinyxml2::XMLElement* itemElement, Item& item);
|
static void parseCraftings(tinyxml2::XMLElement* itemElement, Item& item);
|
||||||
|
|
||||||
bool loadItemsFromXML(const std::string& filepath) {
|
bool loadItemsFromXML(const std::string& filepath, std::unordered_map<uint16_t, Item>& items) {
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
||||||
|
|
||||||
@@ -31,20 +31,20 @@ bool loadItemsFromXML(const std::string& filepath) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_parsedProject.m_items.clear();
|
items.clear();
|
||||||
|
|
||||||
// Parse all items
|
// Parse all items
|
||||||
for (tinyxml2::XMLElement* itemElement = root->FirstChildElement("item");
|
for (tinyxml2::XMLElement* itemElement = root->FirstChildElement("item");
|
||||||
itemElement != nullptr;
|
itemElement != nullptr;
|
||||||
itemElement = itemElement->NextSiblingElement("item")) {
|
itemElement = itemElement->NextSiblingElement("item")) {
|
||||||
parseItem(itemElement);
|
parseItem(itemElement, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Loaded " << g_parsedProject.m_items.size() << " items from XML" << std::endl;
|
std::cout << "Loaded " << items.size() << " items from XML" << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseItem(tinyxml2::XMLElement* itemElement) {
|
void parseItem(tinyxml2::XMLElement* itemElement, std::unordered_map<uint16_t, Item>& items) {
|
||||||
Item item;
|
Item item;
|
||||||
|
|
||||||
// Parse basic attributes
|
// Parse basic attributes
|
||||||
@@ -79,7 +79,7 @@ void parseItem(tinyxml2::XMLElement* itemElement) {
|
|||||||
parseStats(itemElement, item);
|
parseStats(itemElement, item);
|
||||||
parseCraftings(itemElement, item);
|
parseCraftings(itemElement, item);
|
||||||
|
|
||||||
g_parsedProject.m_items[item.id] = std::move(item);
|
items[item.id] = std::move(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseStats(tinyxml2::XMLElement* itemElement, Item& item) {
|
void parseStats(tinyxml2::XMLElement* itemElement, Item& item) {
|
||||||
@@ -157,13 +157,13 @@ void parseCraftings(tinyxml2::XMLElement* itemElement, Item& item) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Item* getItemById(uint16_t id) {
|
const Item* getItemById(uint16_t id, const std::unordered_map<uint16_t, Item>& items) {
|
||||||
auto it = g_parsedProject.m_items.find(id);
|
auto it = items.find(id);
|
||||||
return (it != g_parsedProject.m_items.end()) ? &it->second : nullptr;
|
return (it != items.end()) ? &it->second : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::unordered_map<uint16_t, Item>& getAllItems() {
|
const std::unordered_map<uint16_t, Item>& getAllItems(const std::unordered_map<uint16_t, Item>& items) {
|
||||||
return g_parsedProject.m_items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "configs/loot.h"
|
#include "configs/loot.h"
|
||||||
#include "project_parser.h"
|
#include "project_parser.h"
|
||||||
|
#include "configs/helpers.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace cursebreaker {
|
namespace cursebreaker {
|
||||||
@@ -8,10 +9,10 @@ namespace cursebreaker {
|
|||||||
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
||||||
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
||||||
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
||||||
static void parseLootTable(tinyxml2::XMLElement* lootTableElement);
|
static void parseLootTable(tinyxml2::XMLElement* lootTableElement, std::unordered_map<uint16_t, LootTable>& lootTables);
|
||||||
static void parseEntries(tinyxml2::XMLElement* lootTableElement, LootTable& lootTable);
|
static void parseEntries(tinyxml2::XMLElement* lootTableElement, LootTable& lootTable);
|
||||||
|
|
||||||
bool loadLootTablesFromXML(const std::string& filepath) {
|
bool loadLootTablesFromXML(const std::string& filepath, std::unordered_map<uint16_t, LootTable>& lootTables) {
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
||||||
|
|
||||||
@@ -26,20 +27,20 @@ bool loadLootTablesFromXML(const std::string& filepath) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_parsedProject.m_lootTables.clear();
|
lootTables.clear();
|
||||||
|
|
||||||
// Parse all loot tables
|
// Parse all loot tables
|
||||||
for (tinyxml2::XMLElement* lootTableElement = root->FirstChildElement("lootTable");
|
for (tinyxml2::XMLElement* lootTableElement = root->FirstChildElement("lootTable");
|
||||||
lootTableElement != nullptr;
|
lootTableElement != nullptr;
|
||||||
lootTableElement = lootTableElement->NextSiblingElement("lootTable")) {
|
lootTableElement = lootTableElement->NextSiblingElement("lootTable")) {
|
||||||
parseLootTable(lootTableElement);
|
parseLootTable(lootTableElement, lootTables);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Loaded " << g_parsedProject.m_lootTables.size() << " loot tables from XML" << std::endl;
|
std::cout << "Loaded " << lootTables.size() << " loot tables from XML" << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseLootTable(tinyxml2::XMLElement* lootTableElement) {
|
void parseLootTable(tinyxml2::XMLElement* lootTableElement, std::unordered_map<uint16_t, LootTable>& lootTables) {
|
||||||
LootTable lootTable;
|
LootTable lootTable;
|
||||||
|
|
||||||
// Parse basic attributes
|
// Parse basic attributes
|
||||||
@@ -50,7 +51,7 @@ void parseLootTable(tinyxml2::XMLElement* lootTableElement) {
|
|||||||
// Parse child elements
|
// Parse child elements
|
||||||
parseEntries(lootTableElement, lootTable);
|
parseEntries(lootTableElement, lootTable);
|
||||||
|
|
||||||
g_parsedProject.m_lootTables[lootTable.id] = std::move(lootTable);
|
lootTables[lootTable.id] = std::move(lootTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseEntries(tinyxml2::XMLElement* lootTableElement, LootTable& lootTable) {
|
void parseEntries(tinyxml2::XMLElement* lootTableElement, LootTable& lootTable) {
|
||||||
@@ -69,13 +70,13 @@ void parseEntries(tinyxml2::XMLElement* lootTableElement, LootTable& lootTable)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const LootTable* getLootTableById(uint16_t id) {
|
const LootTable* getLootTableById(uint16_t id, const std::unordered_map<uint16_t, LootTable>& lootTables) {
|
||||||
auto it = g_parsedProject.m_lootTables.find(id);
|
auto it = lootTables.find(id);
|
||||||
return (it != g_parsedProject.m_lootTables.end()) ? &it->second : nullptr;
|
return (it != lootTables.end()) ? &it->second : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::unordered_map<uint16_t, LootTable>& getAllLootTables() {
|
const std::unordered_map<uint16_t, LootTable>& getAllLootTables(const std::unordered_map<uint16_t, LootTable>& lootTables) {
|
||||||
return g_parsedProject.m_lootTables;
|
return lootTables;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "configs/npcs.h"
|
#include "configs/npcs.h"
|
||||||
#include "project_parser.h"
|
#include "project_parser.h"
|
||||||
#include "helpers.hpp"
|
#include "configs/helpers.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -11,11 +11,11 @@ namespace cursebreaker {
|
|||||||
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
||||||
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
||||||
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
||||||
static void parseNPC(tinyxml2::XMLElement* npcElement);
|
static void parseNPC(tinyxml2::XMLElement* npcElement, std::unordered_map<uint16_t, NPC>& npcs);
|
||||||
static void parseStats(tinyxml2::XMLElement* npcElement, NPC& npc);
|
static void parseStats(tinyxml2::XMLElement* npcElement, NPC& npc);
|
||||||
static void parseLoot(tinyxml2::XMLElement* npcElement, NPC& npc);
|
static void parseLoot(tinyxml2::XMLElement* npcElement, NPC& npc);
|
||||||
|
|
||||||
bool loadNPCsFromXML(const std::string& filepath) {
|
bool loadNPCsFromXML(const std::string& filepath, std::unordered_map<uint16_t, NPC>& npcs) {
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
||||||
|
|
||||||
@@ -30,20 +30,20 @@ bool loadNPCsFromXML(const std::string& filepath) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_parsedProject.m_npcs.clear();
|
npcs.clear();
|
||||||
|
|
||||||
// Parse all NPCs
|
// Parse all NPCs
|
||||||
for (tinyxml2::XMLElement* npcElement = root->FirstChildElement("npc");
|
for (tinyxml2::XMLElement* npcElement = root->FirstChildElement("npc");
|
||||||
npcElement != nullptr;
|
npcElement != nullptr;
|
||||||
npcElement = npcElement->NextSiblingElement("npc")) {
|
npcElement = npcElement->NextSiblingElement("npc")) {
|
||||||
parseNPC(npcElement);
|
parseNPC(npcElement, npcs);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Loaded " << g_parsedProject.m_npcs.size() << " NPCs from XML" << std::endl;
|
std::cout << "Loaded " << npcs.size() << " NPCs from XML" << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseNPC(tinyxml2::XMLElement* npcElement) {
|
void parseNPC(tinyxml2::XMLElement* npcElement, std::unordered_map<uint16_t, NPC>& npcs) {
|
||||||
NPC npc;
|
NPC npc;
|
||||||
|
|
||||||
// Parse basic attributes
|
// Parse basic attributes
|
||||||
@@ -63,7 +63,7 @@ void parseNPC(tinyxml2::XMLElement* npcElement) {
|
|||||||
parseStats(npcElement, npc);
|
parseStats(npcElement, npc);
|
||||||
parseLoot(npcElement, npc);
|
parseLoot(npcElement, npc);
|
||||||
|
|
||||||
g_parsedProject.m_npcs[npc.id] = std::move(npc);
|
npcs[npc.id] = std::move(npc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseStats(tinyxml2::XMLElement* npcElement, NPC& npc) {
|
void parseStats(tinyxml2::XMLElement* npcElement, NPC& npc) {
|
||||||
@@ -96,13 +96,13 @@ void parseLoot(tinyxml2::XMLElement* npcElement, NPC& npc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const NPC* getNPCById(uint16_t id) {
|
const NPC* getNPCById(uint16_t id, const std::unordered_map<uint16_t, NPC>& npcs) {
|
||||||
auto it = g_parsedProject.m_npcs.find(id);
|
auto it = npcs.find(id);
|
||||||
return (it != g_parsedProject.m_npcs.end()) ? &it->second : nullptr;
|
return (it != npcs.end()) ? &it->second : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::unordered_map<uint16_t, NPC>& getAllNPCs() {
|
const std::unordered_map<uint16_t, NPC>& getAllNPCs(const std::unordered_map<uint16_t, NPC>& npcs) {
|
||||||
return g_parsedProject.m_npcs;
|
return npcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "configs/shops.h"
|
#include "configs/shops.h"
|
||||||
#include "project_parser.h"
|
#include "project_parser.h"
|
||||||
|
#include "configs/helpers.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace cursebreaker {
|
namespace cursebreaker {
|
||||||
@@ -8,10 +9,10 @@ namespace cursebreaker {
|
|||||||
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
static std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
||||||
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
static int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
||||||
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
static bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
||||||
static void parseShop(tinyxml2::XMLElement* shopElement);
|
static void parseShop(tinyxml2::XMLElement* shopElement, std::unordered_map<uint16_t, Shop>& shops);
|
||||||
static void parseInventory(tinyxml2::XMLElement* shopElement, Shop& shop);
|
static void parseInventory(tinyxml2::XMLElement* shopElement, Shop& shop);
|
||||||
|
|
||||||
bool loadShopsFromXML(const std::string& filepath) {
|
bool loadShopsFromXML(const std::string& filepath, std::unordered_map<uint16_t, Shop>& shops) {
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
||||||
|
|
||||||
@@ -26,20 +27,20 @@ bool loadShopsFromXML(const std::string& filepath) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_parsedProject.m_shops.clear();
|
shops.clear();
|
||||||
|
|
||||||
// Parse all shops
|
// Parse all shops
|
||||||
for (tinyxml2::XMLElement* shopElement = root->FirstChildElement("shop");
|
for (tinyxml2::XMLElement* shopElement = root->FirstChildElement("shop");
|
||||||
shopElement != nullptr;
|
shopElement != nullptr;
|
||||||
shopElement = shopElement->NextSiblingElement("shop")) {
|
shopElement = shopElement->NextSiblingElement("shop")) {
|
||||||
parseShop(shopElement);
|
parseShop(shopElement, shops);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Loaded " << g_parsedProject.m_shops.size() << " shops from XML" << std::endl;
|
std::cout << "Loaded " << shops.size() << " shops from XML" << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseShop(tinyxml2::XMLElement* shopElement) {
|
void parseShop(tinyxml2::XMLElement* shopElement, std::unordered_map<uint16_t, Shop>& shops) {
|
||||||
Shop shop;
|
Shop shop;
|
||||||
|
|
||||||
// Parse basic attributes
|
// Parse basic attributes
|
||||||
@@ -51,7 +52,7 @@ void parseShop(tinyxml2::XMLElement* shopElement) {
|
|||||||
// Parse child elements
|
// Parse child elements
|
||||||
parseInventory(shopElement, shop);
|
parseInventory(shopElement, shop);
|
||||||
|
|
||||||
g_parsedProject.m_shops[shop.id] = std::move(shop);
|
shops[shop.id] = std::move(shop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseInventory(tinyxml2::XMLElement* shopElement, Shop& shop) {
|
void parseInventory(tinyxml2::XMLElement* shopElement, Shop& shop) {
|
||||||
@@ -69,13 +70,13 @@ void parseInventory(tinyxml2::XMLElement* shopElement, Shop& shop) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Shop* getShopById(uint16_t id) {
|
const Shop* getShopById(uint16_t id, const std::unordered_map<uint16_t, Shop>& shops) {
|
||||||
auto it = g_parsedProject.m_shops.find(id);
|
auto it = shops.find(id);
|
||||||
return (it != g_parsedProject.m_shops.end()) ? &it->second : nullptr;
|
return (it != shops.end()) ? &it->second : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::unordered_map<uint16_t, Shop>& getAllShops() {
|
const std::unordered_map<uint16_t, Shop>& getAllShops(const std::unordered_map<uint16_t, Shop>& shops) {
|
||||||
return g_parsedProject.m_shops;
|
return shops;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
||||||
|
|||||||
167
src/main.cpp
167
src/main.cpp
@@ -5,8 +5,21 @@
|
|||||||
#include <SQLiteCpp/SQLiteCpp.h>
|
#include <SQLiteCpp/SQLiteCpp.h>
|
||||||
|
|
||||||
#include "project_parser.h"
|
#include "project_parser.h"
|
||||||
|
#include "project_parser.hpp"
|
||||||
#include "tree_builder.h"
|
#include "tree_builder.h"
|
||||||
#include "assets/scene.hpp"
|
|
||||||
|
// Assets
|
||||||
|
#include "assets/prefab.h"
|
||||||
|
#include "assets/scene.h"
|
||||||
|
|
||||||
|
// Unity types
|
||||||
|
#include "types/game_object.h"
|
||||||
|
#include "types/transform.h"
|
||||||
|
#include "types/prefab_instance.h"
|
||||||
|
|
||||||
|
// Custom types
|
||||||
|
#include "types/Interactable_resource.h"
|
||||||
|
|
||||||
#include "configs/items.h"
|
#include "configs/items.h"
|
||||||
#include "configs/achievements.h"
|
#include "configs/achievements.h"
|
||||||
#include "configs/harvestables.h"
|
#include "configs/harvestables.h"
|
||||||
@@ -17,80 +30,112 @@
|
|||||||
|
|
||||||
using namespace cursebreaker;
|
using namespace cursebreaker;
|
||||||
|
|
||||||
int main() {
|
int main()
|
||||||
|
{
|
||||||
ParseProject("../../CBAssets/_GameAssets/");
|
using AssetTypes = TypeCollection<Prefab, Scene>;
|
||||||
|
using UnityTypes = TypeCollection<GameObject, Transform, PrefabInstance>;
|
||||||
|
using MonobehaviourTypes = TypeCollection<Interactable_Resource>;
|
||||||
|
|
||||||
|
#ifdef linux
|
||||||
|
auto projectRoot = "../../CBAssets/_GameAssets/";
|
||||||
|
#else
|
||||||
|
auto projectRoot = "../CBAssets/_GameAssets/";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto project = ParseProject<AssetTypes, UnityTypes, MonobehaviourTypes>(projectRoot);
|
||||||
|
|
||||||
// build tree
|
// build tree
|
||||||
for (auto& scene : g_parsedProject.m_sceneAssets)
|
for (auto& [guid, scene] : project.GetAssets<Scene>())
|
||||||
{
|
{
|
||||||
BuildTree(scene);
|
BuildTree(project, scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage of ForEachObj to iterate over GameObjects with specific components
|
||||||
|
// Iterate through all scenes and process objects with Transform components
|
||||||
|
for (auto& [guid, scene] : project.GetAssets<Scene>())
|
||||||
|
{
|
||||||
|
auto& sceneData = project.GetSceneData(guid);
|
||||||
|
|
||||||
|
// Example 1: Iterate over all objects with Transform
|
||||||
|
sceneData.ForEachObj<Transform>([&](GameObject& gameObject, Transform& transform)
|
||||||
|
{
|
||||||
|
// Process each object with a transform
|
||||||
|
std::cout << "Found object [" << gameObject.name << "] with transform at position: "
|
||||||
|
<< transform.Position.x << ", " << transform.Position.y << ", " << transform.Position.z << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
sceneData.ForEachObj<Transform, Interactable_Resource>([&](GameObject& gameObject, Transform& transform, Interactable_Resource& interactable)
|
||||||
|
{
|
||||||
|
std::cout << "Found object [" << gameObject.name << "] with transform at position: "
|
||||||
|
<< transform.Position.x << ", " << transform.Position.y << ", " << transform.Position.z << std::endl;
|
||||||
|
std::cout << "Found interactable resource with ID: " << interactable.ID << std::endl;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load configs from XML
|
// Load configs from XML
|
||||||
std::string configPath = "../../CBAssets/_GameAssets/";
|
//std::string configPath = "../../CBAssets/_GameAssets/";
|
||||||
loadItemsFromXML(configPath + "items.xml");
|
//loadItemsFromXML(configPath + "items.xml");
|
||||||
loadAchievementsFromXML(configPath + "achievements.xml");
|
//loadAchievementsFromXML(configPath + "achievements.xml");
|
||||||
loadHarvestablesFromXML(configPath + "harvestables.xml");
|
//loadHarvestablesFromXML(configPath + "harvestables.xml");
|
||||||
loadLootTablesFromXML(configPath + "loot.xml");
|
//loadLootTablesFromXML(configPath + "loot.xml");
|
||||||
loadNPCsFromXML(configPath + "npcs.xml");
|
//loadNPCsFromXML(configPath + "npcs.xml");
|
||||||
loadShopsFromXML(configPath + "shops.xml");
|
//loadShopsFromXML(configPath + "shops.xml");
|
||||||
|
|
||||||
// Create in-memory database
|
//// Create in-memory database
|
||||||
SQLite::Database memDB(":memory:", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
|
//SQLite::Database memDB(":memory:", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
|
||||||
|
|
||||||
// Populate database with configs
|
//// Populate database with configs
|
||||||
std::vector<Item> itemsVec;
|
//std::vector<Item> itemsVec;
|
||||||
for (const auto& pair : getAllItems()) {
|
//for (const auto& pair : getAllItems()) {
|
||||||
itemsVec.push_back(pair.second);
|
// itemsVec.push_back(pair.second);
|
||||||
}
|
//}
|
||||||
PopulateItemDB(memDB, itemsVec);
|
//PopulateItemDB(memDB, itemsVec);
|
||||||
|
|
||||||
std::vector<Achievement> achievementsVec;
|
//std::vector<Achievement> achievementsVec;
|
||||||
for (const auto& pair : getAllAchievements()) {
|
//for (const auto& pair : getAllAchievements()) {
|
||||||
achievementsVec.push_back(pair.second);
|
// achievementsVec.push_back(pair.second);
|
||||||
}
|
//}
|
||||||
PopulateAchievementDB(memDB, achievementsVec);
|
//PopulateAchievementDB(memDB, achievementsVec);
|
||||||
|
|
||||||
std::vector<Harvestable> harvestablesVec;
|
//std::vector<Harvestable> harvestablesVec;
|
||||||
for (const auto& pair : getAllHarvestables()) {
|
//for (const auto& pair : getAllHarvestables()) {
|
||||||
harvestablesVec.push_back(pair.second);
|
// harvestablesVec.push_back(pair.second);
|
||||||
}
|
//}
|
||||||
PopulateHarvestableDB(memDB, harvestablesVec);
|
//PopulateHarvestableDB(memDB, harvestablesVec);
|
||||||
|
|
||||||
std::vector<LootTable> lootVec;
|
//std::vector<LootTable> lootVec;
|
||||||
for (const auto& pair : getAllLootTables()) {
|
//for (const auto& pair : getAllLootTables()) {
|
||||||
lootVec.push_back(pair.second);
|
// lootVec.push_back(pair.second);
|
||||||
}
|
//}
|
||||||
PopulateLootDB(memDB, lootVec);
|
//PopulateLootDB(memDB, lootVec);
|
||||||
|
|
||||||
std::vector<NPC> npcsVec;
|
//std::vector<NPC> npcsVec;
|
||||||
for (const auto& pair : getAllNPCs()) {
|
//for (const auto& pair : getAllNPCs()) {
|
||||||
npcsVec.push_back(pair.second);
|
// npcsVec.push_back(pair.second);
|
||||||
}
|
//}
|
||||||
PopulateNPCDB(memDB, npcsVec);
|
//PopulateNPCDB(memDB, npcsVec);
|
||||||
|
|
||||||
std::vector<Shop> shopsVec;
|
//std::vector<Shop> shopsVec;
|
||||||
for (const auto& pair : getAllShops()) {
|
//for (const auto& pair : getAllShops()) {
|
||||||
shopsVec.push_back(pair.second);
|
// shopsVec.push_back(pair.second);
|
||||||
}
|
//}
|
||||||
PopulateShopDB(memDB, shopsVec);
|
//PopulateShopDB(memDB, shopsVec);
|
||||||
|
|
||||||
// Save to file
|
//// Save to file
|
||||||
{
|
//{
|
||||||
SQLite::Database fileDB("cursebreaker.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
|
// SQLite::Database fileDB("cursebreaker.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
|
||||||
sqlite3* srcHandle = memDB.getHandle();
|
// sqlite3* srcHandle = memDB.getHandle();
|
||||||
sqlite3* dstHandle = fileDB.getHandle();
|
// sqlite3* dstHandle = fileDB.getHandle();
|
||||||
sqlite3_backup* backup = sqlite3_backup_init(dstHandle, "main", srcHandle, "main");
|
// sqlite3_backup* backup = sqlite3_backup_init(dstHandle, "main", srcHandle, "main");
|
||||||
if (backup) {
|
// if (backup) {
|
||||||
sqlite3_backup_step(backup, -1);
|
// sqlite3_backup_step(backup, -1);
|
||||||
sqlite3_backup_finish(backup);
|
// sqlite3_backup_finish(backup);
|
||||||
} else {
|
// } else {
|
||||||
std::cerr << "Failed to initialize backup" << std::endl;
|
// std::cerr << "Failed to initialize backup" << std::endl;
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
std::cout << "Database created and saved to cursebreaker.db" << std::endl;
|
//std::cout << "Database created and saved to cursebreaker.db" << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,465 +1,469 @@
|
|||||||
#include "project_parser.h"
|
#include "project_parser.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
#include <ryml.hpp>
|
#include <ryml.hpp>
|
||||||
#include <ryml_std.hpp>
|
#include <ryml_std.hpp>
|
||||||
#include <c4/format.hpp>
|
#include <c4/format.hpp>
|
||||||
|
|
||||||
|
#include "assets/scene.h"
|
||||||
#include "assets/asset_base.hpp"
|
|
||||||
#include "assets/game_object.hpp"
|
using namespace cursebreaker;
|
||||||
#include "assets/transform.hpp"
|
|
||||||
#include "assets/mesh_filter.hpp"
|
//constexpr uint32_t fnv1a_hash(const char* str, uint32_t hash = 2166136261u)
|
||||||
#include "assets/prefab_instance.hpp"
|
//{
|
||||||
#include "helpers.hpp"
|
// return (*str == '\0') ? hash : fnv1a_hash(str + 1, (hash ^ static_cast<uint32_t>(*str)) * 16777619u);
|
||||||
|
//}
|
||||||
namespace cursebreaker {
|
//
|
||||||
|
//uint32_t fnv1a_hash(c4::csubstr substr)
|
||||||
constexpr uint32_t fnv1a_hash(const char* str, uint32_t hash = 2166136261u)
|
//{
|
||||||
{
|
// uint32_t hash = 2166136261u;
|
||||||
return (*str == '\0') ? hash : fnv1a_hash(str + 1, (hash ^ static_cast<uint32_t>(*str)) * 16777619u);
|
// for (const auto& c : substr)
|
||||||
}
|
// {
|
||||||
|
// hash = (hash ^ static_cast<uint32_t>(c)) * 16777619u;
|
||||||
uint32_t fnv1a_hash(c4::csubstr substr)
|
// }
|
||||||
{
|
// return hash;
|
||||||
uint32_t hash = 2166136261u;
|
//}
|
||||||
for (const auto& c : substr)
|
//
|
||||||
{
|
//uint32_t fnv1a_hash(std::string_view substr)
|
||||||
hash = (hash ^ static_cast<uint32_t>(c)) * 16777619u;
|
//{
|
||||||
}
|
// uint32_t hash = 2166136261u;
|
||||||
return hash;
|
// for (const auto& c : substr)
|
||||||
}
|
// {
|
||||||
|
// hash = (hash ^ static_cast<uint32_t>(c)) * 16777619u;
|
||||||
uint32_t fnv1a_hash(std::string_view substr)
|
// }
|
||||||
{
|
// return hash;
|
||||||
uint32_t hash = 2166136261u;
|
//}
|
||||||
for (const auto& c : substr)
|
|
||||||
{
|
|
||||||
hash = (hash ^ static_cast<uint32_t>(c)) * 16777619u;
|
AssetGUID GetAssetGUIDFromFile(const std::filesystem::path& path)
|
||||||
}
|
{
|
||||||
return hash;
|
// regex match the GUID from the contents of the file
|
||||||
}
|
static auto pattern = std::regex(R"(guid: (\w+))");
|
||||||
|
std::smatch match{};
|
||||||
|
std::ifstream file(path);
|
||||||
AssetGUID GetAssetGUIDFromFile(const std::filesystem::path& path)
|
std::string content{ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
||||||
{
|
if (std::regex_search(content, match, pattern))
|
||||||
/*
|
{
|
||||||
// read file as yaml and get "guid"
|
assert(match[1].length() == 32);
|
||||||
std::ifstream file(path);
|
AssetGUID guid{ std::string_view{ &*match[1].first, static_cast<size_t>(match[1].length()) } };
|
||||||
std::string content{ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
return guid;
|
||||||
|
}
|
||||||
try
|
return AssetGUID{};
|
||||||
{
|
}
|
||||||
ryml::Tree tree = ryml::parse_in_place(c4::substr{ content.data(), content.size() });
|
|
||||||
ryml::ConstNodeRef root = tree.crootref();
|
void ParseSceneYaml(std::string& content,
|
||||||
return ParseAssetGUID(root);
|
bool in_place,
|
||||||
}
|
std::span<std::string_view> assetNames,
|
||||||
catch (const std::runtime_error& e)
|
std::function<void(std::string_view, int64_t, ryml::Tree&&)> callback
|
||||||
{
|
)
|
||||||
std::cerr << "Error parsing asset GUID: " << e.what() << std::endl;
|
{
|
||||||
return AssetGUID{};
|
// std::unordered_map<int64_t, ryml::Tree> Data{};
|
||||||
}
|
|
||||||
*/
|
std::string::size_type start = content.find("---", 0);
|
||||||
|
std::string::size_type end = content.find("---", start + 1);
|
||||||
// regex match the GUID from the contents of the file
|
|
||||||
static auto pattern = std::regex(R"(guid: (\w+))");
|
while (end != std::string::npos)
|
||||||
std::smatch match{};
|
{
|
||||||
std::ifstream file(path);
|
auto document = std::string_view(content.data() + start, content.data() + end);
|
||||||
std::string content{ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
|
||||||
if (std::regex_search(content, match, pattern))
|
// if the document contains "stripped", skip it
|
||||||
{
|
auto str_view = std::string_view(document.data(), document.size());
|
||||||
assert(match[1].length() == 32);
|
auto secondLine = str_view.find('\n') + 1;
|
||||||
AssetGUID guid{ std::string_view{ &*match[1].first, static_cast<size_t>(match[1].length()) } };
|
auto classStr = std::string_view(document.data() + secondLine, str_view.find(':', secondLine) - secondLine);
|
||||||
return guid;
|
|
||||||
}
|
if (str_view.find("stripped") != std::string_view::npos)
|
||||||
return AssetGUID{};
|
goto skip;
|
||||||
}
|
|
||||||
|
if (!assetNames.empty() && std::ranges::find(assetNames, classStr) == assetNames.end())
|
||||||
std::unordered_map<int64_t, ryml::Tree> ParseYamlFile(std::string& content, bool in_place)
|
goto skip;
|
||||||
{
|
|
||||||
std::unordered_map<int64_t, ryml::Tree> Data{};
|
try
|
||||||
|
{
|
||||||
std::string::size_type start = content.find("---", 0);
|
ryml::Tree tree{};
|
||||||
std::string::size_type end = content.find("---", start + 1);
|
|
||||||
|
if (in_place)
|
||||||
while (end != std::string::npos)
|
{
|
||||||
{
|
tree = ryml::parse_in_place(c4::substr{ const_cast<char*>(document.data()), document.size() });
|
||||||
auto document = std::string_view(content.data() + start, content.data() + end);
|
}
|
||||||
|
else
|
||||||
// if the document contains "stripped", skip it
|
{
|
||||||
auto str_view = std::string_view(document.data(), document.size());
|
tree = ryml::parse_in_arena(c4::csubstr{ const_cast<char*>(document.data()), document.size() });
|
||||||
if (str_view.find("stripped") != std::string_view::npos)
|
}
|
||||||
goto skip;
|
auto anchorData = tree.crootref().child(0).val_anchor();
|
||||||
|
int64_t ID{ };
|
||||||
{
|
auto [ptr, ec] = std::from_chars(anchorData.data(), anchorData.data() + anchorData.size(), ID);
|
||||||
auto secondLine = str_view.find('\n') + 1;
|
assert(ec == std::errc{});
|
||||||
auto classStr = std::string_view(document.data() + secondLine, str_view.find(':', secondLine) - secondLine);
|
|
||||||
uint32_t classHash = fnv1a_hash(classStr);
|
callback(classStr, ID, std::move(tree));
|
||||||
|
// Data.emplace(ID, std::move(tree));
|
||||||
constexpr std::array<uint32_t, 5> availableClasses = {
|
}
|
||||||
fnv1a_hash("GameObject"),
|
catch (const std::runtime_error& e)
|
||||||
fnv1a_hash("Transform"),
|
{
|
||||||
fnv1a_hash("MeshFilter"),
|
std::cerr << "Error parsing scene asset: " << e.what() << std::endl;
|
||||||
fnv1a_hash("PrefabInstance"),
|
std::cerr << std::string_view(document.data(), document.size()) << std::endl;
|
||||||
fnv1a_hash("MonoBehaviour")
|
}
|
||||||
};
|
|
||||||
if (std::ranges::find(availableClasses, classHash) == availableClasses.end()) goto skip;
|
skip:;
|
||||||
}
|
|
||||||
|
start = end;
|
||||||
try
|
end = content.find("---", start + 1);
|
||||||
{
|
}
|
||||||
ryml::Tree tree{};
|
|
||||||
|
// return Data;
|
||||||
if (in_place)
|
}
|
||||||
{
|
|
||||||
tree = ryml::parse_in_place(c4::substr{ const_cast<char*>(document.data()), document.size() });
|
/*
|
||||||
}
|
// OLD CODE - Commented out, replaced by new templated design
|
||||||
else
|
|
||||||
{
|
int64_t GetNewID(const Scene& scene, int64_t lastID = 0)
|
||||||
tree = ryml::parse_in_arena(c4::csubstr{ const_cast<char*>(document.data()), document.size() });
|
{
|
||||||
}
|
for (;; ++lastID)
|
||||||
auto anchorData = tree.crootref().child(0).val_anchor();
|
{
|
||||||
int64_t ID{ };
|
if (scene.IDtoAsset.find(lastID) == scene.IDtoAsset.end())
|
||||||
auto [ptr, ec] = std::from_chars(anchorData.data(), anchorData.data() + anchorData.size(), ID);
|
return lastID;
|
||||||
assert(ec == std::errc{});
|
}
|
||||||
|
}
|
||||||
Data.emplace(ID, std::move(tree));
|
|
||||||
}
|
template <typename T>
|
||||||
catch (const std::runtime_error& e)
|
void MergeComponents(Scene& scene, std::unordered_map<int64_t, int64_t>& idMap, std::vector<T>& to, const std::vector<T>& from, int64_t& lastID)
|
||||||
{
|
{
|
||||||
std::cerr << "Error parsing scene asset: " << e.what() << std::endl;
|
for (const auto& asset : from)
|
||||||
std::cerr << std::string_view(document.data(), document.size()) << std::endl;
|
{
|
||||||
}
|
// create a copy of the asset
|
||||||
|
auto assetCopy = asset;
|
||||||
skip:;
|
|
||||||
|
// get a new ID
|
||||||
start = end;
|
lastID = GetNewID(scene, lastID);
|
||||||
end = content.find("---", start + 1);
|
assetCopy.ID = lastID;
|
||||||
|
|
||||||
}
|
// register the new ID to the scene
|
||||||
|
scene.IDtoAsset.emplace(assetCopy.ID, nullptr);
|
||||||
return Data;
|
|
||||||
}
|
// add the asset to the scene
|
||||||
|
to.emplace_back(assetCopy);
|
||||||
int64_t GetNewID(const SceneAsset& scene, int64_t lastID = 0)
|
}
|
||||||
{
|
}
|
||||||
for (;; ++lastID)
|
|
||||||
{
|
void MergeScenes(Scene& to, const Scene& from)
|
||||||
if (scene.IDtoAsset.find(lastID) == scene.IDtoAsset.end())
|
{
|
||||||
return lastID;
|
std::unordered_map<int64_t, int64_t> newIDMap{};
|
||||||
}
|
|
||||||
}
|
// randomize the IDs to avoid conflicts
|
||||||
|
int64_t lastID = std::random_device{}();
|
||||||
template <typename T>
|
MergeComponents(to, newIDMap, to.gameObjects, from.gameObjects, lastID);
|
||||||
void MergeComponents(SceneAsset& scene, std::unordered_map<int64_t, int64_t>& idMap, std::vector<T>& to, const std::vector<T>& from, int64_t& lastID)
|
MergeComponents(to, newIDMap, to.transforms, from.transforms, lastID);
|
||||||
{
|
MergeComponents(to, newIDMap, to.meshFilters, from.meshFilters, lastID);
|
||||||
for (const auto& asset : from)
|
|
||||||
{
|
auto& IDMapLinked = reinterpret_cast<std::unordered_map<int64_t, TypeBase*>&>(newIDMap);
|
||||||
// create a copy of the asset
|
for (auto& gameObject : to.gameObjects) LinkGameObject(IDMapLinked, gameObject);
|
||||||
auto assetCopy = asset;
|
for (auto& transform : to.transforms) LinkTransform(IDMapLinked, transform);
|
||||||
|
for (auto& meshFilter : to.meshFilters) LinkMeshFilter(IDMapLinked, meshFilter);
|
||||||
// get a new ID
|
}
|
||||||
lastID = GetNewID(scene, lastID);
|
|
||||||
assetCopy.ID = lastID;
|
void ParseScene(Scene& asset, const std::unordered_map<int64_t, ryml::Tree>& documents);
|
||||||
|
*/
|
||||||
// register the new ID to the scene
|
|
||||||
scene.IDtoAsset.emplace(assetCopy.ID, nullptr);
|
/*
|
||||||
|
auto split_by_dot(std::string_view str) {
|
||||||
// add the asset to the scene
|
return str
|
||||||
to.emplace_back(assetCopy);
|
| std::views::split('.')
|
||||||
}
|
| std::views::transform([](auto&& rng) {
|
||||||
}
|
return std::string_view(&*rng.begin(), std::ranges::distance(rng));
|
||||||
|
});
|
||||||
void MergeScenes(SceneAsset& to, const SceneAsset& from)
|
}
|
||||||
{
|
|
||||||
std::unordered_map<int64_t, int64_t> newIDMap{};
|
void InstantiatePrefab(Scene& parentScene, const ryml::Tree& instance)
|
||||||
|
{
|
||||||
// randomize the IDs to avoid conflicts
|
auto instanceData = instance.crootref().child(0).child(0);
|
||||||
int64_t lastID = std::random_device{}();
|
|
||||||
MergeComponents(to, newIDMap, to.gameObjects, from.gameObjects, lastID);
|
// parse the prebad UID
|
||||||
MergeComponents(to, newIDMap, to.transforms, from.transforms, lastID);
|
AssetGUID prefabAssetGUID = ParseAssetGUID(instanceData["m_SourcePrefab"]);
|
||||||
MergeComponents(to, newIDMap, to.meshFilters, from.meshFilters, lastID);
|
auto prefabAssetIt = g_parsedProject.m_prefabsMap.find(prefabAssetGUID);
|
||||||
|
if (prefabAssetIt == g_parsedProject.m_prefabsMap.end())
|
||||||
auto& IDMapLinked = reinterpret_cast<std::unordered_map<int64_t, AssetBase*>&>(newIDMap);
|
{
|
||||||
for (auto& gameObject : to.gameObjects) LinkGameObject(IDMapLinked, gameObject);
|
std::cerr << "Prefab asset not found: " << std::endl;
|
||||||
for (auto& transform : to.transforms) LinkTransform(IDMapLinked, transform);
|
return;
|
||||||
for (auto& meshFilter : to.meshFilters) LinkMeshFilter(IDMapLinked, meshFilter);
|
}
|
||||||
}
|
const PrefabAsset& prefabAsset = *prefabAssetIt->second;
|
||||||
|
auto prefabAssetTree = prefabAsset.Data;
|
||||||
void ParseScene(SceneAsset& asset, const std::unordered_map<int64_t, ryml::Tree>& documents);
|
|
||||||
|
// apply the modifications to the prefab asset tree
|
||||||
void InstantiatePrefab(SceneAsset& parentScene, const ryml::Tree& instance)
|
const auto& overrides = instanceData["m_Modification"]["m_Modifications"];
|
||||||
{
|
for (auto override : overrides)
|
||||||
auto instanceData = instance.crootref().child(0).child(0);
|
{
|
||||||
|
int64_t ref = reinterpret_cast<int64_t>(ParseObjectRef<void>(override["target"]));
|
||||||
// parse the prebad UID
|
auto prefabAssetTreeIt = prefabAssetTree.find(ref);
|
||||||
AssetGUID prefabAssetGUID = ParseAssetGUID(instanceData["m_SourcePrefab"]);
|
if (prefabAssetTreeIt == prefabAssetTree.end())
|
||||||
auto prefabAssetIt = g_parsedProject.m_prefabsMap.find(prefabAssetGUID);
|
{
|
||||||
if (prefabAssetIt == g_parsedProject.m_prefabsMap.end())
|
std::cerr << "Prefab asset tree not found: " << ref << std::endl;
|
||||||
{
|
continue;
|
||||||
std::cerr << "Prefab asset not found: " << std::endl;
|
}
|
||||||
return;
|
auto& targetTree = prefabAssetTreeIt->second;
|
||||||
}
|
|
||||||
const PrefabAsset& prefabAsset = *prefabAssetIt->second;
|
const auto propertyPath{ override["propertyPath"].val() };
|
||||||
auto prefabAssetTree = prefabAsset.Data;
|
const auto& overrideValue = override["value"];
|
||||||
|
|
||||||
// apply the modifications to the prefab asset tree
|
try
|
||||||
const auto& overrides = instanceData["m_Modification"]["m_Modifications"];
|
{
|
||||||
for (auto override : overrides)
|
ryml::NodeRef node{ targetTree.rootref().child(0).child(0) };
|
||||||
{
|
for (std::string_view property : split_by_dot(std::string_view{ propertyPath.data(), propertyPath.size() }))
|
||||||
int64_t ref = reinterpret_cast<int64_t>(ParseAssetRef<void>(override["target"]));
|
{
|
||||||
auto prefabAssetTreeIt = prefabAssetTree.find(ref);
|
node = node[c4::substr{ const_cast<char*>(property.data()), property.size() }];
|
||||||
if (prefabAssetTreeIt == prefabAssetTree.end())
|
}
|
||||||
{
|
node << overrideValue.val();
|
||||||
std::cerr << "Prefab asset tree not found: " << ref << std::endl;
|
}
|
||||||
continue;
|
catch (std::exception e){}
|
||||||
}
|
|
||||||
auto& targetTree = prefabAssetTreeIt->second;
|
|
||||||
|
}
|
||||||
const auto propertyPath{ override["propertyPath"].val() };
|
|
||||||
const auto& overrideValue = override["value"];
|
// parse the prefab asset tree into a scene
|
||||||
|
Scene scene{};
|
||||||
// Use lookup_path_or_modify to set the value at the property path
|
ParseScene(scene, prefabAssetTree);
|
||||||
// Start from the root of the document (child(0) is the document root)
|
|
||||||
auto rootId = targetTree.crootref().child(0).id();
|
// get the index of the added transform
|
||||||
targetTree.lookup_path_or_modify(&instance, overrideValue.id(), propertyPath, rootId);
|
size_t addedTransformIndex = scene.transforms.size();
|
||||||
}
|
|
||||||
|
// merge the scenes
|
||||||
// parse the prefab asset tree into a scene
|
MergeScenes(parentScene, scene);
|
||||||
SceneAsset scene{};
|
|
||||||
ParseScene(scene, prefabAssetTree);
|
// find the parent transform and add a the instantiated prebab to the children
|
||||||
|
auto parentID = ParseObjectRef<Transform>(instanceData["m_TransformParent"]);
|
||||||
// get the index of the added transform
|
scene.transforms[addedTransformIndex].Parent = parentID;
|
||||||
size_t addedTransformIndex = scene.transforms.size();
|
auto parentTransform = std::find_if(scene.transforms.begin(), scene.transforms.end(), [parentID](const Transform& asset)
|
||||||
|
{
|
||||||
// merge the scenes
|
return asset.ID == reinterpret_cast<int64_t>(parentID);
|
||||||
MergeScenes(parentScene, scene);
|
});
|
||||||
|
if (parentTransform != scene.transforms.end())
|
||||||
// find the parent transform and add a the instantiated prebab to the children
|
{
|
||||||
auto parentID = ParseAssetRef<TransformAsset>(instanceData["m_TransformParent"]);
|
parentTransform->Children.push_back(reinterpret_cast<Transform*>(scene.transforms[addedTransformIndex].ID));
|
||||||
scene.transforms[addedTransformIndex].Parent = parentID;
|
}
|
||||||
auto parentTransform = std::find_if(scene.transforms.begin(), scene.transforms.end(), [parentID](const TransformAsset& asset)
|
|
||||||
{
|
}
|
||||||
return asset.ID == reinterpret_cast<int64_t>(parentID);
|
*/
|
||||||
});
|
|
||||||
if (parentTransform != scene.transforms.end())
|
/*
|
||||||
{
|
// OLD CODE - Commented out, replaced by new templated design in project_parser.hpp
|
||||||
parentTransform->Children.push_back(reinterpret_cast<TransformAsset*>(scene.transforms[addedTransformIndex].ID));
|
|
||||||
}
|
void LinkAssets(Scene& Scene)
|
||||||
|
{
|
||||||
}
|
std::unordered_map<int64_t, TypeBase*> assetsMap;
|
||||||
|
|
||||||
void LinkAssets(SceneAsset& sceneAsset)
|
for (auto& gameObject : Scene.gameObjects) assetsMap.emplace(gameObject.ID, &gameObject);
|
||||||
{
|
for (auto& transform : Scene.transforms) assetsMap.emplace(transform.ID, &transform);
|
||||||
std::unordered_map<int64_t, AssetBase*> assetsMap;
|
for (auto& meshFilter : Scene.meshFilters) assetsMap.emplace(meshFilter.ID, &meshFilter);
|
||||||
|
|
||||||
for (auto& gameObject : sceneAsset.gameObjects) assetsMap.emplace(gameObject.ID, &gameObject);
|
for (auto& gameObject : Scene.gameObjects) LinkGameObject(assetsMap, gameObject);
|
||||||
for (auto& transform : sceneAsset.transforms) assetsMap.emplace(transform.ID, &transform);
|
for (auto& transform : Scene.transforms) LinkTransform(assetsMap, transform);
|
||||||
for (auto& meshFilter : sceneAsset.meshFilters) assetsMap.emplace(meshFilter.ID, &meshFilter);
|
for (auto& meshFilter : Scene.meshFilters) LinkMeshFilter(assetsMap, meshFilter);
|
||||||
|
}
|
||||||
for (auto& gameObject : sceneAsset.gameObjects) LinkGameObject(assetsMap, gameObject);
|
|
||||||
for (auto& transform : sceneAsset.transforms) LinkTransform(assetsMap, transform);
|
void ParseScene(Scene& asset, const std::unordered_map<int64_t, ryml::Tree>& documents)
|
||||||
for (auto& meshFilter : sceneAsset.meshFilters) LinkMeshFilter(assetsMap, meshFilter);
|
{
|
||||||
}
|
for (auto& [ID, tree] : documents)
|
||||||
|
{
|
||||||
void ParseScene(SceneAsset& asset, const std::unordered_map<int64_t, ryml::Tree>& documents)
|
try
|
||||||
{
|
{
|
||||||
for (auto& [ID, tree] : documents)
|
ryml::ConstNodeRef root = tree.crootref();
|
||||||
{
|
|
||||||
try
|
// Each document should have one root node
|
||||||
{
|
if (root.num_children() > 0)
|
||||||
ryml::ConstNodeRef root = tree.crootref();
|
{
|
||||||
|
auto node = root.child(0).child(0); // The document root
|
||||||
// Each document should have one root node
|
auto assetType = node.key();
|
||||||
if (root.num_children() > 0)
|
|
||||||
{
|
switch (fnv1a_hash(assetType))
|
||||||
auto node = root.child(0).child(0); // The document root
|
{
|
||||||
auto assetType = node.key();
|
case fnv1a_hash("GameObject"): asset.gameObjects.emplace_back(ParseGameObject(node)).ID = ID; break;
|
||||||
|
case fnv1a_hash("Transform"): asset.transforms.emplace_back(ParseTransform(node)).ID = ID; break;
|
||||||
switch (fnv1a_hash(assetType))
|
case fnv1a_hash("MeshFilter"): asset.meshFilters.emplace_back(ParseMeshFilter(node)).ID = ID; break;
|
||||||
{
|
case fnv1a_hash("PrefabInstance"):
|
||||||
case fnv1a_hash("GameObject"): asset.gameObjects.emplace_back(ParseGameObject(node)).ID = ID; break;
|
{
|
||||||
case fnv1a_hash("Transform"): asset.transforms.emplace_back(ParseTransform(node)).ID = ID; break;
|
InstantiatePrefab(asset, tree);
|
||||||
case fnv1a_hash("MeshFilter"): asset.meshFilters.emplace_back(ParseMeshFilter(node)).ID = ID; break;
|
break;
|
||||||
case fnv1a_hash("PrefabInstance"):
|
}
|
||||||
{
|
case fnv1a_hash("MonoBehaviour"):
|
||||||
InstantiatePrefab(asset, tree);
|
{
|
||||||
break;
|
auto scriptGuid = ParseAssetGUID(node["m_Script"]);
|
||||||
}
|
auto it = g_parsedProject.m_scriptToClassHash.find(scriptGuid);
|
||||||
case fnv1a_hash("MonoBehaviour"):
|
if (it != g_parsedProject.m_scriptToClassHash.end())
|
||||||
{
|
{
|
||||||
auto scriptGuid = ParseAssetGUID(node["m_Script"]);
|
switch (it->second)
|
||||||
auto it = g_parsedProject.m_scriptToClassHash.find(scriptGuid);
|
{
|
||||||
if (it != g_parsedProject.m_scriptToClassHash.end())
|
case fnv1a_hash("Interactable_Resource"):
|
||||||
{
|
asset.gameObjects.emplace_back(ParseInteractableResource(node)).ID = ID; break;
|
||||||
switch (it->second)
|
default:
|
||||||
{
|
break;
|
||||||
case fnv1a_hash("Interactable_Resource"):
|
}
|
||||||
default:
|
}
|
||||||
break;
|
}
|
||||||
}
|
default:
|
||||||
}
|
std::cerr << assetType << '\n';
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
std::cerr << assetType << '\n';
|
asset.IDtoAsset.emplace(ID, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
asset.IDtoAsset.emplace(ID, nullptr);
|
}
|
||||||
}
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
}
|
std::cerr << e.what();
|
||||||
catch (const std::exception& e)
|
}
|
||||||
{
|
}
|
||||||
std::cerr << e.what();
|
|
||||||
}
|
LinkAssets(asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
LinkAssets(asset);
|
Scene ParseScene(const AssetPath& path)
|
||||||
}
|
{
|
||||||
|
Scene Scene;
|
||||||
SceneAsset ParseSceneAsset(const AssetPath& path)
|
Scene.path = path.path;
|
||||||
{
|
Scene.name = path.path.stem().string();
|
||||||
SceneAsset sceneAsset;
|
|
||||||
sceneAsset.path = path.path;
|
// read the file
|
||||||
sceneAsset.name = path.path.stem().string();
|
std::ifstream file(path.path);
|
||||||
|
std::string content{ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
||||||
// read the file
|
|
||||||
std::ifstream file(path.path);
|
std::unordered_map<int64_t, ryml::Tree> objects;
|
||||||
std::string content{ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
ParseSceneYaml(content, true, {}, [&objects](std::string_view classStr, int64_t ID, ryml::Tree&& tree) {
|
||||||
|
objects.emplace(ID, std::move(tree));
|
||||||
auto objects = ParseYamlFile(content, true);
|
});
|
||||||
|
|
||||||
ParseScene(sceneAsset, objects);
|
ParseScene(Scene, objects);
|
||||||
|
|
||||||
return sceneAsset;
|
return Scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<SceneAsset> ParseScenes(const std::vector<AssetPath>& sceneFiles)
|
std::vector<Scene> ParseScenes(const std::vector<AssetPath>& sceneFiles)
|
||||||
{
|
{
|
||||||
std::vector<SceneAsset> sceneAssets;
|
std::vector<Scene> Scenes;
|
||||||
for (const auto& sceneFile : sceneFiles)
|
for (const auto& sceneFile : sceneFiles)
|
||||||
{
|
{
|
||||||
sceneAssets.emplace_back(ParseSceneAsset(sceneFile));
|
Scenes.emplace_back(ParseScene(sceneFile));
|
||||||
std::cout << "Parsed: " << sceneFile.path.filename() << '\n';
|
std::cout << "Parsed: " << sceneFile.path.filename() << '\n';
|
||||||
}
|
}
|
||||||
return sceneAssets;
|
return Scenes;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrefabAsset ParsePrefabAsset(const AssetPath& path)
|
PrefabAsset ParsePrefabAsset(const AssetPath& path)
|
||||||
{
|
{
|
||||||
PrefabAsset prefabAsset;
|
PrefabAsset prefabAsset;
|
||||||
prefabAsset.path = path.path;
|
prefabAsset.path = path.path;
|
||||||
prefabAsset.name = path.path.stem().string();
|
prefabAsset.name = path.path.stem().string();
|
||||||
|
|
||||||
{
|
{
|
||||||
// read the file
|
// read the file
|
||||||
std::ifstream file(path.path);
|
std::ifstream file(path.path);
|
||||||
std::string content{ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
std::string content{ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
||||||
|
|
||||||
prefabAsset.Data = ParseYamlFile(content, false);
|
ParseSceneYaml(content, false, {}, [&prefabAsset](std::string_view classStr, int64_t ID, ryml::Tree&& tree) {
|
||||||
}
|
prefabAsset.Data.emplace(ID, std::move(tree));
|
||||||
|
});
|
||||||
return prefabAsset;
|
}
|
||||||
}
|
|
||||||
|
return prefabAsset;
|
||||||
std::vector<PrefabAsset> ParsePrefabs(const std::vector<AssetPath>& prefabFiles, std::unordered_map<AssetGUID, PrefabAsset*>& prefabMap)
|
}
|
||||||
{
|
|
||||||
std::vector<PrefabAsset> prefabAssets;
|
std::vector<PrefabAsset> ParsePrefabs(const std::vector<AssetPath>& prefabFiles, std::unordered_map<AssetGUID, PrefabAsset*>& prefabMap)
|
||||||
for (const auto& prefabFile : prefabFiles)
|
{
|
||||||
{
|
std::vector<PrefabAsset> prefabAssets;
|
||||||
prefabAssets.emplace_back(ParsePrefabAsset(prefabFile));
|
for (const auto& prefabFile : prefabFiles)
|
||||||
}
|
{
|
||||||
|
prefabAssets.emplace_back(ParsePrefabAsset(prefabFile));
|
||||||
prefabMap.reserve(prefabAssets.size());
|
}
|
||||||
for (size_t i = 0; i < prefabAssets.size(); ++i)
|
|
||||||
{
|
prefabMap.reserve(prefabAssets.size());
|
||||||
prefabMap.emplace(prefabFiles[i].GUID, &prefabAssets[i]);
|
for (size_t i = 0; i < prefabAssets.size(); ++i)
|
||||||
}
|
{
|
||||||
return prefabAssets;
|
prefabMap.emplace(prefabFiles[i].GUID, &prefabAssets[i]);
|
||||||
}
|
}
|
||||||
|
return prefabAssets;
|
||||||
void ParseDirectory(ParsedProject& project)
|
}
|
||||||
{
|
*/
|
||||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(project.m_projectRoot))
|
|
||||||
{
|
/*
|
||||||
std::string extension = entry.path().extension().string();
|
void ParseDirectory(ParsedProjectBase& project)
|
||||||
|
{
|
||||||
if (!entry.is_directory())
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(project.m_projectRoot))
|
||||||
{
|
{
|
||||||
if (extension == ".unity")
|
std::string extension = entry.path().extension().string();
|
||||||
{
|
|
||||||
project.m_sceneFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
if (!entry.is_directory())
|
||||||
}
|
{
|
||||||
else if (extension == ".prefab")
|
if (extension == ".unity")
|
||||||
{
|
{
|
||||||
project.m_prefabFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
project.m_sceneFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
||||||
}
|
}
|
||||||
else if (extension == ".cs")
|
else if (extension == ".prefab")
|
||||||
{
|
{
|
||||||
auto guid = GetAssetGUIDFromFile(entry.path().string() + ".meta");
|
project.m_prefabFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
||||||
project.m_scriptFiles.push_back(AssetPath{ entry.path(), guid });
|
}
|
||||||
project.m_scriptToClassHash.emplace(guid, fnv1a_hash(entry.path().filename().string()));
|
else if (extension == ".cs")
|
||||||
}
|
{
|
||||||
continue;
|
auto guid = GetAssetGUIDFromFile(entry.path().string() + ".meta");
|
||||||
if (extension == ".png" || extension == ".jpg" || extension == ".jpeg" || extension == ".bmp" || extension == ".tiff" || extension == ".webp")
|
project.m_scriptFiles.push_back(AssetPath{ entry.path(), guid });
|
||||||
{
|
project.m_scriptToClassName.emplace(guid, entry.path().filename().string());
|
||||||
project.m_imageFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
}
|
||||||
}
|
continue;
|
||||||
else if (extension == ".fbx" || extension == ".obj" || extension == ".glb" || extension == ".gltf")
|
if (extension == ".png" || extension == ".jpg" || extension == ".jpeg" || extension == ".bmp" || extension == ".tiff" || extension == ".webp")
|
||||||
{
|
{
|
||||||
project.m_meshFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
project.m_imageFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
||||||
}
|
}
|
||||||
}
|
else if (extension == ".fbx" || extension == ".obj" || extension == ".glb" || extension == ".gltf")
|
||||||
}
|
{
|
||||||
}
|
project.m_meshFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
||||||
|
}
|
||||||
ParsedProject g_parsedProject;
|
}
|
||||||
|
}
|
||||||
void ParseProject(const std::filesystem::path& projectRoot)
|
}
|
||||||
{
|
ParsedProject g_parsedProject;
|
||||||
g_parsedProject = ParsedProject{};
|
*/
|
||||||
ParsedProject& project = g_parsedProject;
|
|
||||||
project.m_projectRoot = projectRoot;
|
/*
|
||||||
|
// OLD CODE - Commented out, replaced by new templated design in project_parser.hpp
|
||||||
ParseDirectory(project);
|
|
||||||
|
void ParseProject(const std::filesystem::path& projectRoot, ParsedProjectBase& project)
|
||||||
auto tilesFilesFilter = std::views::filter([](const AssetPath& path)
|
{
|
||||||
{
|
|
||||||
#ifdef _DEBUG
|
|
||||||
static auto pattern = std::regex(R"(Tiles\/10_3)");
|
auto tilesFilesFilter = std::views::filter([](const AssetPath& path)
|
||||||
#else
|
{
|
||||||
static auto pattern = std::regex(R"(Tiles\/\d+_\d+)");
|
#ifdef _DEBUG
|
||||||
#endif
|
static auto pattern = std::regex(R"(Tiles\/10_3)");
|
||||||
return std::regex_search(path.path.generic_string(), pattern);
|
#else
|
||||||
});
|
static auto pattern = std::regex(R"(Tiles\/\d+_\d+)");
|
||||||
auto tilesFiles = project.m_sceneFiles | tilesFilesFilter;
|
#endif
|
||||||
|
return std::regex_search(path.path.generic_string(), pattern);
|
||||||
auto prefabFilter = std::views::filter([](const AssetPath& path)
|
});
|
||||||
{
|
auto tilesFiles = project.m_sceneFiles | tilesFilesFilter;
|
||||||
constexpr std::string_view prefabPath = "_GameAssets/Prefabs";
|
|
||||||
return path.path.generic_string().find(prefabPath) != std::string::npos;
|
auto prefabFilter = std::views::filter([](const AssetPath& path)
|
||||||
});
|
{
|
||||||
auto prefabFiles = project.m_prefabFiles | prefabFilter;
|
constexpr std::string_view prefabPath = "_GameAssets/Prefabs";
|
||||||
|
return path.path.generic_string().find(prefabPath) != std::string::npos;
|
||||||
project.m_prefabAssets = ParsePrefabs(std::vector<AssetPath>(prefabFiles.begin(), prefabFiles.end()), project.m_prefabsMap);
|
});
|
||||||
|
auto prefabFiles = project.m_prefabFiles | prefabFilter;
|
||||||
project.m_sceneAssets = ParseScenes(std::vector<AssetPath>(tilesFiles.begin(), tilesFiles.end()));
|
|
||||||
}} // namespace cursebreaker
|
project.m_prefabAssets = ParsePrefabs(std::vector<AssetPath>(prefabFiles.begin(), prefabFiles.end()), project.m_prefabsMap);
|
||||||
|
|
||||||
|
project.m_Scenes = ParseScenes(std::vector<AssetPath>(tilesFiles.begin(), tilesFiles.end()));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -1,54 +1,35 @@
|
|||||||
#include "tree_builder.h"
|
#include "tree_builder.h"
|
||||||
#include "assets/scene.hpp"
|
#include "assets/scene.h"
|
||||||
#include "assets/transform.hpp"
|
#include "types/transform.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
glm::mat4 composeMatrix(const glm::vec3& pos, const glm::vec3& scale, const glm::quat& rotation) {
|
glm::mat4 composeMatrix(const glm::vec3& pos, const glm::vec3& scale, const glm::quat& rotation) {
|
||||||
// Create translation matrix
|
// Create translation matrix
|
||||||
glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0f), pos);
|
glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0f), pos);
|
||||||
|
|
||||||
// Convert quaternion to rotation matrix
|
// Convert quaternion to rotation matrix
|
||||||
glm::mat4 rotationMatrix = glm::mat4_cast(rotation);
|
glm::mat4 rotationMatrix = glm::mat4_cast(rotation);
|
||||||
|
|
||||||
// Create scale matrix
|
// Create scale matrix
|
||||||
glm::mat4 scaleMatrix = glm::scale(glm::mat4(1.0f), scale);
|
glm::mat4 scaleMatrix = glm::scale(glm::mat4(1.0f), scale);
|
||||||
|
|
||||||
// Combine: translation * rotation * scale
|
// Combine: translation * rotation * scale
|
||||||
return translationMatrix * rotationMatrix * scaleMatrix;
|
return translationMatrix * rotationMatrix * scaleMatrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildTreeHelper(TransformAsset& transform, const glm::mat4& parentMatrix)
|
void BuildTreeHelper(Transform& transform, const glm::mat4& parentMatrix)
|
||||||
{
|
{
|
||||||
for (auto& child : transform.Children)
|
for (auto& child : transform.Children)
|
||||||
{
|
{
|
||||||
auto childMatrix = composeMatrix(transform.Position, transform.Scale, transform.Rotation);
|
auto childMatrix = composeMatrix(transform.Position, transform.Scale, transform.Rotation);
|
||||||
transform.GlobalMatrix = parentMatrix * childMatrix;
|
transform.GlobalMatrix = parentMatrix * childMatrix;
|
||||||
|
|
||||||
BuildTreeHelper(*child, transform.GlobalMatrix);
|
BuildTreeHelper(*child, transform.GlobalMatrix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildTree(SceneAsset& sceneAsset)
|
|
||||||
{
|
|
||||||
// find transforms with no parent
|
|
||||||
std::vector<TransformAsset*> rootTransforms;
|
|
||||||
|
|
||||||
for (auto& transform : sceneAsset.transforms)
|
|
||||||
{
|
|
||||||
if (transform.Parent == nullptr)
|
|
||||||
{
|
|
||||||
rootTransforms.push_back(&transform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// build tree
|
|
||||||
for (auto rootTransform : rootTransforms)
|
|
||||||
{
|
|
||||||
BuildTreeHelper(*rootTransform, glm::mat4(1.0f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
45
src/types/game_object.cpp
Normal file
45
src/types/game_object.cpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#include "types/game_object.h"
|
||||||
|
#include "project_parser.h"
|
||||||
|
#include "configs/helpers.h"
|
||||||
|
|
||||||
|
void GameObject::Parse(const ryml::ConstNodeRef& node, ParseContext& context)
|
||||||
|
{
|
||||||
|
node["m_Name"] >> name;
|
||||||
|
node["m_Layer"] >> layer;
|
||||||
|
node["m_TagString"] >> tag;
|
||||||
|
|
||||||
|
auto components = node["m_Component"];
|
||||||
|
auto it = components.begin();
|
||||||
|
|
||||||
|
transform = ParseObjectRef<Transform>((*it)["component"]);
|
||||||
|
for (; it != components.end(); ++it)
|
||||||
|
{
|
||||||
|
Components.push_back(ParseObjectRef<TypeBase>((*it)["component"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameObject::Link(const std::unordered_map<int64_t, TypeBase*>& assetsMap)
|
||||||
|
{
|
||||||
|
LinkObjectRef<Transform>(assetsMap, transform);
|
||||||
|
|
||||||
|
// Clear the component mask before rebuilding it
|
||||||
|
ComponentMask.reset();
|
||||||
|
|
||||||
|
for (int i = static_cast<int>(Components.size()) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (!LinkObjectRef<TypeBase>(assetsMap, Components[i]))
|
||||||
|
{
|
||||||
|
std::swap(Components[i], Components.back());
|
||||||
|
Components.pop_back();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Set the bit for this component's type
|
||||||
|
if (Components[i]->TypeID >= 0 && Components[i]->TypeID < 64)
|
||||||
|
{
|
||||||
|
ComponentMask.set(Components[i]->TypeID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/types/interactable_resource.cpp
Normal file
6
src/types/interactable_resource.cpp
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include "types/interactable_resource.h"
|
||||||
|
|
||||||
|
void Interactable_Resource::Parse(const ryml::ConstNodeRef& node, ParseContext& context)
|
||||||
|
{
|
||||||
|
node["typeId"] >> ResourceID;
|
||||||
|
}
|
||||||
14
src/types/mesh_filter.cpp
Normal file
14
src/types/mesh_filter.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include "types/mesh_filter.h"
|
||||||
|
#include "types/game_object.h"
|
||||||
|
#include "project_parser.h"
|
||||||
|
|
||||||
|
void MeshFilter::Parse(const ryml::ConstNodeRef& node, ParseContext& context)
|
||||||
|
{
|
||||||
|
Mesh = ParseAssetGUID(node["m_Mesh"]);
|
||||||
|
GameObject = ParseObjectRef<::GameObject>(node["m_GameObject"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshFilter::Link(const std::unordered_map<int64_t, TypeBase*>& assetsMap)
|
||||||
|
{
|
||||||
|
LinkObjectRef<::GameObject>(assetsMap, GameObject);
|
||||||
|
}
|
||||||
77
src/types/prefab_instance.cpp
Normal file
77
src/types/prefab_instance.cpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include "types/prefab_instance.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
|
#include "assets/prefab.h"
|
||||||
|
|
||||||
|
auto split_by_dot(std::string_view str) {
|
||||||
|
return str
|
||||||
|
| std::views::split('.')
|
||||||
|
| std::views::transform([](auto&& rng) {
|
||||||
|
return std::string_view(&*rng.begin(), std::ranges::distance(rng));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrefabInstance::Parse(const ryml::ConstNodeRef& node, ParseContext& context)
|
||||||
|
{
|
||||||
|
// parse the prefab UID
|
||||||
|
AssetGUID prefabAssetGUID = ParseAssetGUID(node["m_SourcePrefab"]);
|
||||||
|
auto prefabAssetIt = context.Assets.GetAsset<Prefab>(prefabAssetGUID);
|
||||||
|
if (prefabAssetIt == nullptr)
|
||||||
|
{
|
||||||
|
std::cerr << "Prefab asset not found: " << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto prefabAssetTree = prefabAssetIt->Data;
|
||||||
|
|
||||||
|
// apply the modifications to the prefab asset tree
|
||||||
|
const auto& overrides = node["m_Modification"]["m_Modifications"];
|
||||||
|
for (auto override : overrides)
|
||||||
|
{
|
||||||
|
int64_t ref = reinterpret_cast<int64_t>(ParseObjectRef<void>(override["target"]));
|
||||||
|
auto prefabAssetTreeIt = prefabAssetTree.find(ref);
|
||||||
|
if (prefabAssetTreeIt == prefabAssetTree.end())
|
||||||
|
{
|
||||||
|
std::cerr << "Prefab asset tree not found: " << ref << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto& targetTree = prefabAssetTreeIt->second;
|
||||||
|
|
||||||
|
const auto propertyPath{ override["propertyPath"].val() };
|
||||||
|
const auto& overrideValue = override["value"];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ryml::NodeRef node{ targetTree.rootref().child(0).child(0) };
|
||||||
|
for (std::string_view property : split_by_dot(std::string_view{ propertyPath.data(), propertyPath.size() }))
|
||||||
|
{
|
||||||
|
node = node[c4::substr{ const_cast<char*>(property.data()), property.size() }];
|
||||||
|
}
|
||||||
|
node << overrideValue.val();
|
||||||
|
}
|
||||||
|
catch (std::exception e){}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the prefab asset tree into a scene
|
||||||
|
Scene scene{}
|
||||||
|
ParseScene(scene, prefabAssetTree);
|
||||||
|
|
||||||
|
// get the index of the added transform
|
||||||
|
size_t addedTransformIndex = scene.transforms.size();
|
||||||
|
|
||||||
|
// merge the scenes
|
||||||
|
MergeScenes(context.MainScene, scene);
|
||||||
|
|
||||||
|
// find the parent transform and add the instantiated prefab to the children
|
||||||
|
auto parentID = ParseObjectRef<Transform>(node["m_TransformParent"]);
|
||||||
|
scene.transforms[addedTransformIndex].Parent = parentID;
|
||||||
|
auto parentTransform = std::find_if(scene.transforms.begin(), scene.transforms.end(), [parentID](const Transform& asset)
|
||||||
|
{
|
||||||
|
return asset.ID == reinterpret_cast<int64_t>(parentID);
|
||||||
|
});
|
||||||
|
if (parentTransform != scene.transforms.end())
|
||||||
|
{
|
||||||
|
parentTransform->Children.push_back(reinterpret_cast<Transform*>(scene.transforms[addedTransformIndex].ID));
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/types/transform.cpp
Normal file
51
src/types/transform.cpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#include "types/transform.h"
|
||||||
|
#include "types/game_object.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transform::Parse(const ryml::ConstNodeRef& node, ParseContext& context)
|
||||||
|
{
|
||||||
|
Rotation = ParseQuaternion(node["m_LocalRotation"]);
|
||||||
|
Position = ParseVector3(node["m_LocalPosition"]);
|
||||||
|
Scale = ParseVector3(node["m_LocalScale"]);
|
||||||
|
|
||||||
|
Parent = ParseObjectRef<::Transform>(node["m_Father"]);
|
||||||
|
GameObject = ParseObjectRef<::GameObject>(node["m_GameObject"]);
|
||||||
|
|
||||||
|
auto children = node["m_Children"];
|
||||||
|
for (const auto& child : children)
|
||||||
|
{
|
||||||
|
Children.push_back(ParseObjectRef<::Transform>(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transform::Link(const std::unordered_map<int64_t, TypeBase*>& assetsMap)
|
||||||
|
{
|
||||||
|
LinkObjectRef<::Transform>(assetsMap, Parent);
|
||||||
|
for (int i = static_cast<int>(Children.size()) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (!LinkObjectRef<::Transform>(assetsMap, Children[i]))
|
||||||
|
{
|
||||||
|
std::swap(Children[i], Children.back());
|
||||||
|
Children.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LinkObjectRef<::GameObject>(assetsMap, GameObject);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user