diff --git a/Deer/Include/DeerCore/Universe.h b/Deer/Include/DeerCore/Universe.h index c3a9d2e..1bd8722 100644 --- a/Deer/Include/DeerCore/Universe.h +++ b/Deer/Include/DeerCore/Universe.h @@ -2,14 +2,16 @@ #include "DeerCore/Serialization/WorldSettings.h" #include "DeerCore/Tools/Path.h" +#include + namespace Deer { class World; class WorldSettings; namespace Universe { World* createWorld(const WorldSettings&); - World* loadWorldFromJson(const WorldSettings&, WorldSerializationSettings&, const Path&); + World* loadWorldFromJson(const WorldSettings&, WorldSerializationSettings&, const Path&); void saveWorldInJson(World*, WorldSerializationSettings& serializationSettings, const Path& path); void destroyAllWorlds(); diff --git a/Deer/Include/DeerRender/DataManagment.h b/Deer/Include/DeerRender/DataManagment.h index 677d90a..49578f7 100644 --- a/Deer/Include/DeerRender/DataManagment.h +++ b/Deer/Include/DeerRender/DataManagment.h @@ -42,13 +42,11 @@ namespace Deer { template class StorageBackend { public: - static StorageData loadData(const std::string& location); - static void saveData(const std::string& location, const StorageData& data); + static StorageData loadData(const std::string& id); + static void saveData(const std::string& id, const StorageData& data); - static StorageMetadata loadMetadata(const std::string& location); - static void saveMetadata(const StorageMetadata& metadata, const std::string& location); - - static std::vector indexResources(const std::string& location); + static StorageMetadata loadMetadata(const std::string& id); + static void saveMetadata(const StorageMetadata& metadata, const std::string& id); }; template diff --git a/DeerStudio/headers/DeerStudio/StudioResources.h b/DeerStudio/headers/DeerStudio/StudioResources.h new file mode 100644 index 0000000..bcaea1e --- /dev/null +++ b/DeerStudio/headers/DeerStudio/StudioResources.h @@ -0,0 +1,36 @@ +#pragma once +#include "cereal/cereal.hpp" + +#include + +namespace Deer { + enum class ResourceType : int { + NONE = 0, + MESH = 1, + SHADER = 2, + TEXTURE = 3 + }; + + struct ResourceInformation { + time_t lastUpdate; + std::string resourcePath; + ResourceType resourceType; + uint32_t resourceId; + + // Metadata... + + template + void serialize(Archive& archive) { + archive(cereal::make_nvp("Id", resourceId)); + archive(cereal::make_nvp("Path", resourcePath)); + archive(cereal::make_nvp("Type", resourceType)); + } + }; + + namespace StudioResources { + ResourceInformation* getResourceInfoFromPath(const std::string& path); + ResourceInformation* getResourceInfoFromId(uint32_t resource); + + void scanResources(); + } // namespace StudioResources +} // namespace Deer \ No newline at end of file diff --git a/DeerStudio/src/DeerStudio/DeerStudio.cpp b/DeerStudio/src/DeerStudio/DeerStudio.cpp index 120a78d..e96fdb3 100644 --- a/DeerStudio/src/DeerStudio/DeerStudio.cpp +++ b/DeerStudio/src/DeerStudio/DeerStudio.cpp @@ -10,6 +10,7 @@ #include "DeerStudio/EditorDataImporter.h" #include "DeerStudio/EditorDataSource.h" #include "DeerStudio/ResourceDataSource.h" +#include "DeerStudio/StudioResources.h" // TMP #include "DeerCore/EntityEnviroment.h" @@ -52,6 +53,7 @@ namespace Deer { world = Universe::createWorld(worldSettings); while (world) { + StudioResources::scanResources(); StudioPanel::setWorld(world); world->execute(); } diff --git a/DeerStudio/src/DeerStudio/StudioResources.cpp b/DeerStudio/src/DeerStudio/StudioResources.cpp new file mode 100644 index 0000000..83f380b --- /dev/null +++ b/DeerStudio/src/DeerStudio/StudioResources.cpp @@ -0,0 +1,234 @@ +#include "DeerStudio/StudioResources.h" +#include "DeerCore/Tools/Path.h" + +#include "cereal/archives/json.hpp" + +#include "cereal/types/string.hpp" +#include "cereal/types/vector.hpp" + +#include "DeerCore/Log.h" + +#include +#include +#include +#include + +namespace cereal { + + template + std::string save_minimal(Archive const&, const Deer::ResourceType& type) { + switch (type) { + case Deer::ResourceType::NONE: + return "NONE"; + case Deer::ResourceType::MESH: + return "MESH"; + case Deer::ResourceType::SHADER: + return "SHADER"; + case Deer::ResourceType::TEXTURE: + return "TEXTURE"; + } + return "NONE"; + } + + template + void load_minimal(Archive const&, Deer::ResourceType& type, const std::string& value) { + if (value == "MESH") + type = Deer::ResourceType::MESH; + else if (value == "SHADER") + type = Deer::ResourceType::SHADER; + else if (value == "TEXTURE") + type = Deer::ResourceType::TEXTURE; + else + type = Deer::ResourceType::NONE; + } + +} // namespace cereal + +namespace Deer { + struct FolderInfo { + std::string name; + std::string parent; + std::vector resources; + std::vector subfolders; + }; + + namespace StudioResources { + std::vector resources; + std::unordered_map resourcePathToIndex; + std::unordered_map resourceIdToIndex; + + std::vector folders; + std::unordered_map resourceIdToFolderIndex; + std::unordered_map folderPathToFolderIndex; + + ResourceType getResourceType(const std::string& dir) { + Path path(dir); + std::string extension = path.extension().string(); + + if (extension == ".obj" || extension == ".fbx" || extension == ".dae" || + extension == ".3ds" || extension == ".ply" || extension == ".stl" || + extension == ".glb" || extension == ".gltf") { + return ResourceType::MESH; + } + + if (extension == ".glsl") + return ResourceType::SHADER; + + if (extension == ".png") + return ResourceType::TEXTURE; + + return ResourceType::NONE; + }; + } // namespace StudioResources + + ResourceInformation* StudioResources::getResourceInfoFromPath(const std::string& path) { + if (!resourceMap.contains(path)) + return nullptr; + + return resources[resourceMap[path]]; + } + + ResourceInformation* StudioResources::getResourceInfoFromId(uint32_t resource) { + if (!resourceIdToIndex.contains(resource)) + return nullptr; + + return resources[resourceIdToIndex[resource]]; + } + + size_t getFolderIndex(std::string folder) { + size_t folderIndex; + + FolderInfo & folder; + if (folderPathToFolderIndex.contains(folderIndex)) { + folderIndex = folderPathToFolderIndex[folderPathStr]; + folder = folders[folderIndex]; + } else { + newFolder.name = folderPath.filename().string(); + newFolder.fullPath = folderPathStr; + folders.push_back(std::move(newFolder)); + folder = folders.back(); + + folderPathToFolderIndex[folderPathStr] = folders.size() - 1; + } + } + + void addFolderToFolder(std::string childFolder, std::string parentFolder) { + FolderInfo & folder; + if (folderPathToFolderIndex.contains(folderIndex)) { + folderIndex = folderPathToFolderIndex[folderPathStr]; + folder = folders[folderIndex]; + } else { + newFolder.name = folderPath.filename().string(); + newFolder.fullPath = folderPathStr; + folders.push_back(std::move(newFolder)); + folder = folders.back(); + + folderPathToFolderIndex[folderPathStr] = folders.size() - 1; + } + } + + void StudioResources::scanResources() { + resources.clear(); + resourcePathToIndex.clear(); + resourceIdToIndex.clear(); + + folders.clear(); + resourceIdToFolderIndex.clear(); + folderPathToFolderIndex.clear(); + + Path resourcePath("Resources"); + Path resourceJsonPath = resourcePath / "ResourceData.json"; + if (std::filesystem::exists(resourceJsonPath)) { + std::ifstream inputStream(resourceJsonPath); + if (inputStream) { + try { + cereal::JSONInputArchive archive(inputStream); + archive(cereal::make_nvp("Resources", resources)); + } catch (cereal::Exception& exception) { + DEER_CORE_ERROR("Error loading resources JSON \n{}", exception.what()); + } + } + } + + for (auto& file : std::filesystem::recursive_directory_iterator(resourcePath)) { + Path filePath = file.path(); + ResourceType type = getResourceType(filePath); + + if (type == ResourceType::NONE) + continue; + + std::string pathId = filePath.lexically_relative(resourcePath).generic_string().c_str(); + ResourceInformation* referencingResource = getResourceInfoFromPath(pathId); + + if (!referencingResource) { + ResourceInformation resourceInformation = { + .resourceId = std::random_device{}(), + .resourcePath = pathId, + .resourceType = type}; + + resources.push_back(resourceInformation); + referencingResource = &resources.back(); + } + + auto ftime = file.last_write_time(); + auto sctp = std::chrono::time_point_cast(ftime - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now()); + + referencingResource->lastUpdate = std::chrono::system_clock::to_time_t(sctp); + } + + for (size_t i = 0; i < resources.size(); i++) { + const ResourceInformation& ri = resources[i]; + + resourceIdToIndex[ri.resourceId] = i; + resourcePathToIndex[ri.resourcePath] = i; + + Path resourceFullPath = ri.resourcePath; + Path folderPath = resourceFullPath.parent_path(); + + std::string folderPathStr = folderPath.generic_string(); + size_t folderIndex; + + FolderInfo & folder; + if (folderPathToFolderIndex.contains(folderIndex)) { + folderIndex = folderPathToFolderIndex[folderPathStr]; + folder = folders[folderIndex]; + } else { + newFolder.name = folderPath.filename().string(); + newFolder.fullPath = folderPathStr; + folders.push_back(std::move(newFolder)); + folder = folders.back(); + + folderPathToFolderIndex[folderPathStr] = folders.size() - 1; + } + + newFolder.resources.push_back(ri.resourceId); + + // Check if folder already exists + auto it = folderPathToIndex.find(folderPathStr); + if (it != folderPathToIndex.end()) { + folderIndex = it->second; + } else { + // Create new folder + } + + // If folder exists, just append resource + if (it != folderPathToIndex.end()) { + folders[folderIndex].resources.push_back(ri.resourceId); + } + + // Map resource -> folder ID for fast lookup + resourceIdToFolderIndex[ri.resourceId] = static_cast(folderIndex); + } + + { + std::ofstream outputStream(resourceJsonPath); + if (!outputStream) { + DEER_CORE_ERROR("Failed opening {} for writing", resourceJsonPath.string()); + return; + } + + cereal::JSONOutputArchive archive(outputStream); + archive(cereal::make_nvp("Resources", resources)); + } + } // namespace Deer +} // namespace Deer \ No newline at end of file diff --git a/Resources/ResourceData.json b/Resources/ResourceData.json new file mode 100644 index 0000000..be35df5 --- /dev/null +++ b/Resources/ResourceData.json @@ -0,0 +1,269 @@ +{ + "Resources": [ + { + "Id": 3069814959, + "Path": "shader_uv.glsl", + "Type": "SHADER" + }, + { + "Id": 104674944, + "Path": "shader_darkBlue.glsl", + "Type": "SHADER" + }, + { + "Id": 2366771542, + "Path": "normal.glsl", + "Type": "SHADER" + }, + { + "Id": 2694733599, + "Path": "train-locomotive-a.obj", + "Type": "MESH" + }, + { + "Id": 4256579129, + "Path": "Kenney/OBJ format/ship-ocean-liner.obj", + "Type": "MESH" + }, + { + "Id": 3739632882, + "Path": "Kenney/OBJ format/boat-fan.obj", + "Type": "MESH" + }, + { + "Id": 3532984180, + "Path": "Kenney/OBJ format/boat-row-small.obj", + "Type": "MESH" + }, + { + "Id": 1925528477, + "Path": "Kenney/OBJ format/boat-tug-b.obj", + "Type": "MESH" + }, + { + "Id": 2275934537, + "Path": "Kenney/OBJ format/ship-large.obj", + "Type": "MESH" + }, + { + "Id": 95651136, + "Path": "Kenney/OBJ format/boat-speed-a.obj", + "Type": "MESH" + }, + { + "Id": 2559379043, + "Path": "Kenney/OBJ format/cargo-pile-b.obj", + "Type": "MESH" + }, + { + "Id": 4114347144, + "Path": "Kenney/OBJ format/cargo-container-a.obj", + "Type": "MESH" + }, + { + "Id": 1349019805, + "Path": "Kenney/OBJ format/ship-ocean-liner-small.obj", + "Type": "MESH" + }, + { + "Id": 1178824383, + "Path": "Kenney/OBJ format/boat-house-b.obj", + "Type": "MESH" + }, + { + "Id": 1756987657, + "Path": "Kenney/OBJ format/boat-house-d.obj", + "Type": "MESH" + }, + { + "Id": 2681564794, + "Path": "Kenney/OBJ format/cargo-pile-a.obj", + "Type": "MESH" + }, + { + "Id": 3501520615, + "Path": "Kenney/OBJ format/cargo-container-b.obj", + "Type": "MESH" + }, + { + "Id": 3889696810, + "Path": "Kenney/OBJ format/boat-tug-c.obj", + "Type": "MESH" + }, + { + "Id": 852751301, + "Path": "Kenney/OBJ format/boat-sail-b.obj", + "Type": "MESH" + }, + { + "Id": 1602018002, + "Path": "Kenney/OBJ format/boat-house-a.obj", + "Type": "MESH" + }, + { + "Id": 3198781315, + "Path": "Kenney/OBJ format/ship-small-ghost.obj", + "Type": "MESH" + }, + { + "Id": 311499906, + "Path": "Kenney/OBJ format/cargo-container-c.obj", + "Type": "MESH" + }, + { + "Id": 4215786342, + "Path": "Kenney/OBJ format/boat-speed-h.obj", + "Type": "MESH" + }, + { + "Id": 4100856350, + "Path": "Kenney/OBJ format/ramp.obj", + "Type": "MESH" + }, + { + "Id": 3814904649, + "Path": "Kenney/OBJ format/boat-speed-e.obj", + "Type": "MESH" + }, + { + "Id": 2019280713, + "Path": "Kenney/OBJ format/arrow.obj", + "Type": "MESH" + }, + { + "Id": 1507032232, + "Path": "Kenney/OBJ format/boat-tow-b.obj", + "Type": "MESH" + }, + { + "Id": 198501249, + "Path": "Kenney/OBJ format/ramp-wide.obj", + "Type": "MESH" + }, + { + "Id": 4136095909, + "Path": "Kenney/OBJ format/boat-tow-a.obj", + "Type": "MESH" + }, + { + "Id": 1565745400, + "Path": "Kenney/OBJ format/boat-sail-a.obj", + "Type": "MESH" + }, + { + "Id": 919433681, + "Path": "Kenney/OBJ format/boat-fishing-small.obj", + "Type": "MESH" + }, + { + "Id": 1374732749, + "Path": "Kenney/OBJ format/boat-speed-i.obj", + "Type": "MESH" + }, + { + "Id": 434341145, + "Path": "Kenney/OBJ format/arrow-standing.obj", + "Type": "MESH" + }, + { + "Id": 500626431, + "Path": "Kenney/OBJ format/boat-tug-a.obj", + "Type": "MESH" + }, + { + "Id": 3009449850, + "Path": "Kenney/OBJ format/boat-speed-c.obj", + "Type": "MESH" + }, + { + "Id": 1928134758, + "Path": "Kenney/OBJ format/boat-speed-d.obj", + "Type": "MESH" + }, + { + "Id": 815434663, + "Path": "Kenney/OBJ format/gate-finish.obj", + "Type": "MESH" + }, + { + "Id": 584294637, + "Path": "Kenney/OBJ format/boat-speed-g.obj", + "Type": "MESH" + }, + { + "Id": 2079718166, + "Path": "Kenney/OBJ format/boat-speed-b.obj", + "Type": "MESH" + }, + { + "Id": 2407778965, + "Path": "Kenney/OBJ format/buoy.obj", + "Type": "MESH" + }, + { + "Id": 2607493592, + "Path": "Kenney/OBJ format/ship-cargo-c.obj", + "Type": "MESH" + }, + { + "Id": 2561363166, + "Path": "Kenney/OBJ format/ship-cargo-a.obj", + "Type": "MESH" + }, + { + "Id": 3016804499, + "Path": "Kenney/OBJ format/boat-speed-j.obj", + "Type": "MESH" + }, + { + "Id": 2318332737, + "Path": "Kenney/OBJ format/ship-small.obj", + "Type": "MESH" + }, + { + "Id": 3596619090, + "Path": "Kenney/OBJ format/boat-speed-f.obj", + "Type": "MESH" + }, + { + "Id": 2289035505, + "Path": "Kenney/OBJ format/boat-row-large.obj", + "Type": "MESH" + }, + { + "Id": 4228274168, + "Path": "Kenney/OBJ format/gate.obj", + "Type": "MESH" + }, + { + "Id": 202169080, + "Path": "Kenney/OBJ format/ship-cargo-b.obj", + "Type": "MESH" + }, + { + "Id": 2037323258, + "Path": "Kenney/OBJ format/buoy-flag.obj", + "Type": "MESH" + }, + { + "Id": 3045406746, + "Path": "Kenney/OBJ format/boat-house-c.obj", + "Type": "MESH" + }, + { + "Id": 1098769283, + "Path": "Kenney/Textures/colormap.png", + "Type": "TEXTURE" + }, + { + "Id": 360224249, + "Path": "monkey.obj", + "Type": "MESH" + }, + { + "Id": 3705973655, + "Path": "shader.glsl", + "Type": "SHADER" + } + ] +} \ No newline at end of file diff --git a/q b/q deleted file mode 100644 index e69de29..0000000