Loading...
Searching...
No Matches
AssetRegistry.cpp
Go to the documentation of this file.
1//
2// Created by denzel on 31/10/2025.
3//
4
6
7#include <fstream>
8#include <iostream>
9#include <ranges>
10#include <thread>
11
12#include "json.hpp"
13
14namespace hellfire {
15 AssetRegistry::AssetRegistry(const std::filesystem::path &registry_file, const std::filesystem::path &project_root) : registry_file_(registry_file), project_root_(project_root) {
16 load();
17 }
18
20 save();
21 }
22
23 AssetID AssetRegistry::register_asset(const std::filesystem::path &filepath) {
25 return register_asset(filepath, type);
26 }
27
28 AssetID AssetRegistry::register_asset(const std::filesystem::path &filepath, AssetType type) {
29 auto absolute_path = std::filesystem::absolute(filepath);
30
31 // Check if already registered
32 auto relative_path = to_relative_path(absolute_path);
33 auto it = path_to_uuid_.find(relative_path);
34 if (it != path_to_uuid_.end()) {
35 // Update last modified time
36 if (auto* asset = &assets_.at(it->second)) {
37 asset->last_modified = get_file_last_modified(absolute_path);
38 }
39 return it->second;
40 }
41
42 // Generate UUID from relative path
43 const AssetID uuid = generate_uuid(relative_path);
44
45 const AssetMetadata metadata{
46 .uuid = uuid,
47 .filepath = relative_path,
48 .type = type,
49 .name = filepath.stem().string(),
51 };
52
53 assets_[uuid] = metadata;
54 path_to_uuid_[relative_path] = uuid;
55
56 return uuid;
57 }
58
59 void AssetRegistry::unregister_asset(const AssetID uuid) {
60 if (const auto it = assets_.find(uuid); it != assets_.end()) {
61 path_to_uuid_.erase(it->second.filepath);
62 assets_.erase(it);
63 }
64 }
65
66 std::vector<AssetID> AssetRegistry::register_directory(const std::filesystem::path &directory_path, bool recursive) {
67 std::vector<AssetID> registered_assets;
68 auto absolute_dir = to_absolute_path(directory_path);
69
70 if (!std::filesystem::exists(absolute_dir)) {
71 return registered_assets;
72 }
73
74 auto iterator = std::filesystem::recursive_directory_iterator(absolute_dir);
75 for (const auto& entry : iterator) {
76 if (entry.is_regular_file()) {
77 AssetType type = get_type_from_extension(entry.path());
78 if (type != AssetType::UNKNOWN) {
79 AssetID id = register_asset(entry.path(), type);
80 registered_assets.push_back(id);
81 }
82 }
83 }
84
85 return registered_assets;
86 }
87
89 for (auto &metadata: assets_ | std::views::values) {
90 auto absolute_path = to_absolute_path(metadata.filepath);
91 if (std::filesystem::exists(absolute_path)) {
92 metadata.last_modified = get_file_last_modified(absolute_path);
93 }
94 }
95 }
96
98 const auto it = assets_.find(uuid);
99 if (it != assets_.end()) {
100 return it->second;
101 }
102 return std::nullopt;
103 }
104
105 std::optional<uint64_t> AssetRegistry::get_uuid_by_path(const std::filesystem::path &filepath) {
106 const auto relative_path = to_relative_path(filepath);
107 const auto it = path_to_uuid_.find(relative_path);
108 if (it != path_to_uuid_.end()) {
109 return it->second;
110 }
111 return std::nullopt;
112 }
113
115 std::vector<AssetMetadata> result;
116 for (const auto &metadata: assets_ | std::views::values) {
117 if (metadata.type == type) {
118 result.push_back(metadata);
119 }
120 }
121 return result;
122 }
123
125 std::vector<AssetMetadata> result;
126 result.reserve(assets_.size());
127 for (const auto &metadata: assets_ | std::views::values) {
128 result.push_back(metadata);
129 }
130 return result;
131 }
132
133 bool AssetRegistry::asset_exists(AssetID uuid) const {
134 return assets_.contains(uuid);
135 }
136
137 std::filesystem::path AssetRegistry::get_absolute_path(AssetID uuid) {
138 if (const auto it = assets_.find(uuid); it != assets_.end()) {
139 return to_absolute_path(it->second.filepath);
140 }
141 return {};
142 }
143
144 std::filesystem::path AssetRegistry::get_relative_path(AssetID uuid) {
145 if (const auto it = assets_.find(uuid); it != assets_.end()) {
146 return to_relative_path(it->second.filepath);
147 }
148 return {};
149 }
150
151 bool AssetRegistry::has_asset_changed(const AssetID uuid) const {
152 auto it = assets_.find(uuid);
153 if (it != assets_.end()) {
154 auto absolute_path = to_absolute_path(it->second.filepath);
155 if (std::filesystem::exists(absolute_path)) {
156 const auto current_modified = get_file_last_modified(absolute_path);
157 return current_modified != it->second.last_modified;
158 }
159 }
160 return false;
161 }
162
164 std::vector<AssetID> modified;
165 for (const auto &uuid: assets_ | std::views::keys) {
166 if (has_asset_changed(uuid)) {
167 modified.push_back(uuid);
168 }
169 }
170 return modified;
171 }
172
174 nlohmann::json j;
175 j["version"] = "1.0";
176 j["assets"] = nlohmann::json::array();
177
178 for (const auto &metadata: assets_ | std::views::values) {
179 nlohmann::json asset_json;
180 asset_json["uuid"] = metadata.uuid;
181 asset_json["path"] = metadata.filepath.string();
182 asset_json["type"] = metadata.type;
183 asset_json["name"] = metadata.name;
184 asset_json["last_modified"] = metadata.last_modified;
185 j["assets"].push_back(asset_json);
186 }
187
188 std::ofstream file(registry_file_);
189 file << j.dump(4);
190 }
191
193 if (!std::filesystem::exists(registry_file_)) {
194 return false;
195 }
196
197 try {
198 std::ifstream file(registry_file_);
199 nlohmann::json j;
200 file >> j;
201
202 if (j.contains("assets")) {
203 for (const auto& asset_json : j["assets"]) {
204 AssetMetadata metadata{
205 .uuid = asset_json["uuid"].get<AssetID>(),
206 .filepath = asset_json["path"].get<std::string>(),
207 .type = asset_json["type"].get<AssetType>(),
208 .name = asset_json["name"].get<std::string>(),
209 .last_modified = asset_json["last_modified"].get<uint64_t>(),
210 };
211
212 assets_[metadata.uuid] = metadata;
213 path_to_uuid_[metadata.filepath] = metadata.uuid;
214 }
215 }
216 return true;
217 } catch (const std::exception& e) {
218 std::cerr << "ERROR::ASSETREGISTRY::LOAD:: " << e.what() << std::endl;
219 return false;
220 }
221 }
222
224 assets_.clear();
225 path_to_uuid_.clear();
226 }
227
228 void AssetRegistry::set_project_root(const std::filesystem::path &project_root) {
229 project_root_ = project_root;
230 }
231
232 AssetType AssetRegistry::get_type_from_extension(const std::filesystem::path &filepath) {
233 static const std::unordered_map<std::string, AssetType> extension_map = {
234 {".png", AssetType::TEXTURE},
235 {".jpg", AssetType::TEXTURE},
236 {".jpeg", AssetType::TEXTURE},
237 {".obj", AssetType::MODEL},
238 {".gltf", AssetType::MODEL},
239 {".glb", AssetType::MODEL},
240 {".fbx", AssetType::MODEL},
241 {".hfmodel", AssetType::MODEL},
242 {".hfmat", AssetType::MATERIAL},
243 {".hfmesh", AssetType::MESH},
244 {".hfscene", AssetType::SCENE},
245 {".frag", AssetType::SHADER},
246 {".vert", AssetType::SHADER},
247 {".glsl", AssetType::SHADER},
248 {".shader", AssetType::SHADER}
249 };
250
251 auto extension = filepath.extension().string();
252 std::ranges::transform(extension, extension.begin(), tolower); // To make sure it's case-insensitive
253 const auto it = extension_map.find(extension);
254
255 return (it != extension_map.end()) ? it->second : AssetType::UNKNOWN;
256 }
257
258 AssetID AssetRegistry::generate_uuid(const std::filesystem::path& filepath) {
259 std::hash<std::string> hasher;
260 uint64_t id = hasher(filepath.string());
261 return (id == INVALID_ASSET_ID) ? 1 : id;
262 }
263
264 std::filesystem::path AssetRegistry::to_relative_path(const std::filesystem::path &absolute_path) const {
265 return std::filesystem::relative(absolute_path, project_root_);
266 }
267
268 std::filesystem::path AssetRegistry::to_absolute_path(const std::filesystem::path &relative_path) const {
269 return project_root_ / relative_path;
270 }
271
273 path_to_uuid_.clear();
274 for (const auto& [uuid, metadata] : assets_) {
275 path_to_uuid_[metadata.filepath] = uuid;
276 }
277 }
278
279 AssetID AssetRegistry::get_file_last_modified(const std::filesystem::path &filepath) const {
280 if (std::filesystem::exists(filepath)) {
281 const auto ftime = std::filesystem::last_write_time(filepath);
282 return ftime.time_since_epoch().count();
283 }
284 return 0;
285 }
286}
Registry for storing assets.
std::optional< AssetID > get_uuid_by_path(const std::filesystem::path &filepath)
AssetID generate_uuid(const std::filesystem::path &filepath)
void set_project_root(const std::filesystem::path &project_root)
std::filesystem::path get_absolute_path(AssetID uuid)
std::unordered_map< std::filesystem::path, AssetID > path_to_uuid_
std::filesystem::path registry_file_
AssetID register_asset(const std::filesystem::path &filepath, AssetType type)
std::unordered_map< AssetID, AssetMetadata > assets_
std::filesystem::path to_absolute_path(const std::filesystem::path &relative_path) const
static AssetType get_type_from_extension(const std::filesystem::path &filepath)
std::filesystem::path to_relative_path(const std::filesystem::path &absolute_path) const
AssetID register_asset(const std::filesystem::path &filepath)
std::vector< AssetID > register_directory(const std::filesystem::path &directory_path, bool recursive)
std::vector< AssetMetadata > get_assets_by_type(AssetType type)
std::filesystem::path get_relative_path(AssetID uuid)
bool asset_exists(AssetID uuid) const
std::vector< AssetID > get_modified_assets() const
AssetRegistry(const std::filesystem::path &registry_file, const std::filesystem::path &project_root)
std::optional< AssetMetadata > get_asset(AssetID uuid) const
std::filesystem::path project_root_
std::vector< AssetMetadata > get_all_assets() const
uint64_t get_file_last_modified(const std::filesystem::path &filepath) const
constexpr AssetID INVALID_ASSET_ID
std::filesystem::path filepath