Brenta Engine 1.2
Loading...
Searching...
No Matches
asset.cpp
1// SPDX-License-Identifier: MIT
2// Author: Giovanni Santini
3// Mail: giovanni.santini@proton.me
4// Github: @San7o
5
6#include <brenta/asset.hpp>
7#include <brenta/logger.hpp>
8#include <brenta/renderer/model.hpp>
9#include <brenta/renderer/opengl/texture.hpp>
10#include <brenta/renderer/material.hpp>
11#include <brenta/scene.hpp>
12#include <brenta/font.hpp>
13#include <brenta/sound.hpp>
14
15using namespace brenta;
16
17//
18// Static storage
19//
20
21std::unordered_map<AssetManager::AssetId,
22 AssetManager::Asset<Model>> AssetManager::models;
23std::unordered_map<AssetManager::AssetId,
24 AssetManager::Asset<Texture>> AssetManager::textures;
25std::unordered_map<AssetManager::AssetId,
26 AssetManager::Asset<Material>> AssetManager::materials;
27std::unordered_map<AssetManager::AssetId,
28 AssetManager::AssetOwned<Scene>> AssetManager::scenes;
29std::unordered_map<AssetManager::AssetId,
30 AssetManager::Asset<Shader>> AssetManager::shaders;
31std::unordered_map<AssetManager::AssetId,
32 AssetManager::Asset<Font>> AssetManager::fonts;
33std::unordered_map<AssetManager::AssetId,
35AssetManager::sound_assets;
36
37bool AssetManager::hotreload_active = false;
38FilesystemWatcher AssetManager::fswatcher;
39tenno::jthread AssetManager::hotreload_thread;
40tenno::mutex AssetManager::hotreload_pending_mutex;
41tenno::vector<AssetManager::HotReloadItem> AssetManager::hotreload_pending;
42std::unordered_map<std::filesystem::path, AssetManager::HotReloadItem>
43AssetManager::hotreload_entries;
44
45//
46// Member functions
47//
48
49template<>
50tenno::shared_ptr<Model>
51AssetManager::new_asset<Model>(const AssetId& id,
52 Model::Builder &builder)
53{
54 auto ptr = tenno::make_shared<Model>(builder.build());
55 ptr.set_cache(false);
56 AssetManager::models[id] = {builder, ptr};
57
58 auto watch_paths = builder.get_watch_paths();
59 for (auto& watch_path : watch_paths)
60 {
61 AssetManager::hotreload_entries[watch_path] = {
62 .type = AssetType::Model,
63 .id = id,
64 };
65 }
66
67 DEBUG("AssetManager: create new model {}", id);
68
69 return ptr;
70}
71
72template<>
73tenno::shared_ptr<Texture>
74AssetManager::new_asset<Texture>(const AssetId& id,
75 Texture::Builder &builder)
76{
77 auto ptr = tenno::make_shared<Texture>(builder.build());
78 ptr.set_cache(false);
79 AssetManager::textures[id] = {builder, ptr};
80
81 auto watch_paths = builder.get_watch_paths();
82 for (auto& watch_path : watch_paths)
83 {
84 AssetManager::hotreload_entries[watch_path] = {
85 .type = AssetType::Texture,
86 .id = id,
87 };
88 }
89
90 DEBUG("AssetManager: create new texture {}", id);
91
92 return ptr;
93}
94
95template<>
96tenno::shared_ptr<Material>
97AssetManager::new_asset<Material>(const AssetId& id,
98 Material::Builder &builder)
99{
100 auto ptr = tenno::make_shared<Material>(builder.build());
101 ptr.set_cache(false);
102 AssetManager::materials[id] = {builder, ptr};
103
104 DEBUG("AssetManager: create new material {}", id);
105
106 return ptr;
107}
108
109template<>
110tenno::shared_ptr<Font>
111AssetManager::new_asset<Font>(const AssetId& id,
112 Font::Builder &builder)
113{
114 auto ptr = tenno::make_shared<Font>(builder.build());
115 ptr.set_cache(false);
116 AssetManager::fonts[id] = {builder, ptr};
117
118 auto watch_paths = builder.get_watch_paths();
119 for (auto& watch_path : watch_paths)
120 {
121 AssetManager::hotreload_entries[watch_path] = {
122 .type = AssetType::Font,
123 .id = id,
124 };
125 }
126
127 DEBUG("AssetManager: create new font {}", id);
128
129 return ptr;
130}
131
132template<>
133tenno::shared_ptr<Scene>
134AssetManager::new_asset<Scene>(const AssetId& id,
135 Scene::Builder &builder)
136{
137 auto ptr = tenno::make_shared<Scene>(builder.build());
138 ptr.set_cache(false);
139 AssetManager::scenes[id] = {builder, ptr};
140
141 DEBUG("AssetManager: create new scene {}", id);
142
143 return ptr;
144}
145
146template<>
147tenno::shared_ptr<Shader>
148AssetManager::new_asset<Shader>(const AssetId& id,
149 Shader::Builder &builder)
150{
151 auto maybe_shader = builder.build();
152 if (!maybe_shader) return nullptr;
153
154 auto shader =
155 tenno::make_shared<Shader>(tenno::move(maybe_shader.value()));
156 shader.set_cache(false);
157 AssetManager::shaders[id] = {builder, shader};
158
159 DEBUG("AssetManager: create new shader {}", id);
160
161 return shader;
162}
163
164template<>
165tenno::shared_ptr<SoundAsset>
166AssetManager::new_asset<SoundAsset>(const AssetId& id,
167 SoundAsset::Builder &builder)
168{
169 auto maybe_sound_asset = builder.build();
170 if (!maybe_sound_asset)
171 return nullptr;
172
173 auto ptr = tenno::make_shared<SoundAsset>(tenno::move(maybe_sound_asset.value()));
174 ptr.set_cache(false);
175 AssetManager::sound_assets[id] = {builder, ptr};
176
177 DEBUG("AssetManager: create new sound {}", id);
178
179 return ptr;
180}
181
182template<>
183tenno::shared_ptr<Texture> AssetManager::get<Texture>(const AssetId& id)
184{
185 if (!AssetManager::textures.contains(id)) return nullptr;
186
187 if (tenno::shared_ptr<Texture> ptr =
188 AssetManager::textures[id].ptr.lock())
189 return ptr;
190
191 return nullptr;
192}
193
194template<>
195tenno::shared_ptr<Model> AssetManager::get<Model>(const AssetId& id)
196{
197 if (!AssetManager::models.contains(id)) return nullptr;
198
199 if (tenno::shared_ptr<Model> ptr =
200 AssetManager::models[id].ptr.lock())
201 return ptr;
202
203 return nullptr;
204}
205
206template<>
207tenno::shared_ptr<Shader> AssetManager::get<Shader>(const AssetId& id)
208{
209 if (!AssetManager::shaders.contains(id)) return nullptr;
210
211 if (tenno::shared_ptr<Shader> ptr =
212 AssetManager::shaders[id].ptr.lock())
213 return ptr;
214
215 return nullptr;
216}
217
218template<>
219tenno::shared_ptr<Material> AssetManager::get<Material>(const AssetId& id)
220{
221 if (!AssetManager::materials.contains(id)) return nullptr;
222
223 if (tenno::shared_ptr<Material> ptr =
224 AssetManager::materials[id].ptr.lock())
225 return ptr;
226
227 return nullptr;
228}
229
230template<>
231tenno::shared_ptr<Scene> AssetManager::get<Scene>(const AssetId& id)
232{
233 if (!AssetManager::scenes.contains(id)) return nullptr;
234 return AssetManager::scenes[id].ptr;
235}
236
237template<>
238tenno::shared_ptr<Font> AssetManager::get<Font>(const AssetId& id)
239{
240 if (!AssetManager::fonts.contains(id)) return nullptr;
241
242 if (tenno::shared_ptr<Font> ptr =
243 AssetManager::fonts[id].ptr.lock())
244 return ptr;
245
246 return nullptr;
247}
248
249template<>
250tenno::shared_ptr<SoundAsset>
251AssetManager::get<SoundAsset>(const AssetId& id)
252{
253 if (!AssetManager::sound_assets.contains(id)) return nullptr;
254 return AssetManager::sound_assets[id].ptr;
255}
256
257template<>
258bool AssetManager::reload<Model>(const AssetId& id)
259{
260 if (!AssetManager::models.contains(id)) return false;
261
262 Asset<Model>& asset = AssetManager::models[id];
263 tenno::shared_ptr<Model> new_model =
264 tenno::make_shared<Model>(asset.builder.build());
265 asset.ptr.swap_ptr(new_model);
266
267 DEBUG("AssetManager: reloaded model {}", id);
268
269 return true;
270}
271
272template<>
273bool AssetManager::reload<Texture>(const AssetId& id)
274{
275 if (!AssetManager::textures.contains(id)) return false;
276
277 Asset<Texture>& asset = AssetManager::textures[id];
278 tenno::shared_ptr<Texture> new_texture =
279 tenno::make_shared<Texture>(asset.builder.build());
280 asset.ptr.swap_ptr(new_texture);
281
282 DEBUG("AssetManager: reloaded texture {}", id);
283
284 return true;
285}
286
287template<>
288bool AssetManager::reload<Material>(const AssetId& id)
289{
290 if (!AssetManager::materials.contains(id)) return false;
291
292 Asset<Material>& asset = AssetManager::materials[id];
293 tenno::shared_ptr<Material> new_material =
294 tenno::make_shared<Material>(asset.builder.build());
295 asset.ptr.swap_ptr(new_material);
296
297 DEBUG("AssetManager: reloaded material {}", id);
298
299 return true;
300}
301
302template<>
303bool AssetManager::reload<Font>(const AssetId& id)
304{
305 if (!AssetManager::fonts.contains(id)) return false;
306
307 Asset<Font>& asset = AssetManager::fonts[id];
308 tenno::shared_ptr<Font> new_font =
309 tenno::make_shared<Font>(asset.builder.build());
310 asset.ptr.swap_ptr(new_font);
311
312 DEBUG("AssetManager: reloaded font {}", id);
313
314 return true;
315}
316
317template<>
318bool AssetManager::reload<Scene>(const AssetId& id)
319{
320 if (!AssetManager::scenes.contains(id)) return false;
321
322 AssetOwned<Scene>& asset = AssetManager::scenes[id];
323 tenno::shared_ptr<Scene> new_scene =
324 tenno::make_shared<Scene>(asset.builder.build());
325 asset.ptr.swap_ptr(new_scene);
326
327 DEBUG("AssetManager: reloaded scene {}", id);
328
329 return true;
330}
331
332template<>
333bool AssetManager::reload<Shader>(const AssetId& id)
334{
335 if (!AssetManager::shaders.contains(id)) return false;
336
337 Asset<Shader>& asset = AssetManager::shaders[id];
338 auto maybe_shader = asset.builder.build();
339 if (!maybe_shader) return false;
340
341 tenno::shared_ptr<Shader> new_shader =
342 tenno::make_shared<Shader>(tenno::move(*new_shader));
343 asset.ptr.swap_ptr(new_shader);
344
345 DEBUG("AssetManager: reloaded shader {}", id);
346
347 return true;
348}
349
350template<>
351bool AssetManager::reload<SoundAsset>(const AssetId& id)
352{
353 if (!AssetManager::sound_assets.contains(id)) return false;
354
355 AssetOwned<SoundAsset>& asset = AssetManager::sound_assets[id];
356 auto maybe_sound_asset = asset.builder.build();
357 if (!maybe_sound_asset)
358 return false;
359
360 tenno::shared_ptr<SoundAsset> new_sound =
361 tenno::make_shared<SoundAsset>(tenno::move(maybe_sound_asset.value()));
362 asset.ptr.swap_ptr(new_sound);
363
364 DEBUG("AssetManager: reloaded sound {}", id);
365
366 return true;
367}
368
369void AssetManager::clear()
370{
371 AssetManager::models.clear();
372 AssetManager::textures.clear();
373 AssetManager::materials.clear();
374 AssetManager::scenes.clear();
375 AssetManager::shaders.clear();
376 AssetManager::fonts.clear();
377 AssetManager::sound_assets.clear();
378
379 return;
380}
381
382void AssetManager::hotreload_activate()
383{
384 if (AssetManager::hotreload_active) return;
385
386 AssetManager::hotreload_active = true;
387
388 AssetManager::hotreload_thread = tenno::jthread([](){
389 DEBUG("AssetManager: created hotreloading thread");
390
391 AssetManager::fswatcher.init();
392
393 for (auto& [path, _] : AssetManager::hotreload_entries)
394 {
395 AssetManager::fswatcher.add(path, {FilesystemWatcher::Event::Modify});
396 DEBUG("AssetManager: hotreloading setup {}", path.string());
397 }
398
399 while(AssetManager::hotreload_active)
400 {
401 auto maybe_event = AssetManager::fswatcher.watch();
402 if (!maybe_event) continue;
403
404 DEBUG("AssetManager: hotreloading received event");
405
406 auto event = *maybe_event;
407 if (AssetManager::hotreload_entries.contains(event))
408 {
409 tenno::lock_guard<tenno::mutex> lock(AssetManager::hotreload_pending_mutex);
410 AssetManager::hotreload_pending.push_back(AssetManager::hotreload_entries[event]);
411 DEBUG("AssetManager: hotreloading detected change in {}", event.string());
412 }
413 }
414
415 return;
416 });
417
418 DEBUG("AssetManager: hotreloading activated");
419}
420
421void AssetManager::hotreload_deactivate()
422{
423 if (!AssetManager::hotreload_active) return;
424
425 AssetManager::hotreload_active = false;
426
427 AssetManager::hotreload_thread.request_stop();
428 AssetManager::fswatcher.destroy();
429
430 DEBUG("AssetManager: hotreloading deactivated");
431}
432
433void AssetManager::hotreload_update()
434{
435 if (!AssetManager::hotreload_active) return;
436
437 tenno::lock_guard<tenno::mutex> lock(AssetManager::hotreload_pending_mutex);
438
439 for (auto& item : AssetManager::hotreload_pending)
440 {
441 DEBUG("AssetManager: hotreloading now {}", item.id);
442 switch(item.type)
443 {
444 case AssetType::Model: AssetManager::reload<Model>(item.id); break;
445 case AssetType::Texture: AssetManager::reload<Texture>(item.id); break;
446 case AssetType::Shader: AssetManager::reload<Shader>(item.id); break;
447 case AssetType::Font: AssetManager::reload<Font>(item.id); break;
448 default: break;
449 }
450 }
451
452 AssetManager::hotreload_pending.clear();
453}