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>
15using namespace brenta;
21std::unordered_map<AssetManager::AssetId,
23std::unordered_map<AssetManager::AssetId,
25std::unordered_map<AssetManager::AssetId,
27std::unordered_map<AssetManager::AssetId,
29std::unordered_map<AssetManager::AssetId,
31std::unordered_map<AssetManager::AssetId,
33std::unordered_map<AssetManager::AssetId,
35AssetManager::sound_assets;
37bool AssetManager::hotreload_active =
false;
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;
50tenno::shared_ptr<Model>
51AssetManager::new_asset<Model>(
const AssetId&
id,
54 auto ptr = tenno::make_shared<Model>(builder.build());
56 AssetManager::models[id] = {builder, ptr};
58 auto watch_paths = builder.get_watch_paths();
59 for (
auto& watch_path : watch_paths)
61 AssetManager::hotreload_entries[watch_path] = {
62 .type = AssetType::Model,
67 DEBUG(
"AssetManager: create new model {}",
id);
73tenno::shared_ptr<Texture>
74AssetManager::new_asset<Texture>(
const AssetId&
id,
77 auto ptr = tenno::make_shared<Texture>(builder.build());
79 AssetManager::textures[id] = {builder, ptr};
81 auto watch_paths = builder.get_watch_paths();
82 for (
auto& watch_path : watch_paths)
84 AssetManager::hotreload_entries[watch_path] = {
85 .type = AssetType::Texture,
90 DEBUG(
"AssetManager: create new texture {}",
id);
96tenno::shared_ptr<Material>
97AssetManager::new_asset<Material>(
const AssetId&
id,
100 auto ptr = tenno::make_shared<Material>(builder.build());
101 ptr.set_cache(
false);
102 AssetManager::materials[id] = {builder, ptr};
104 DEBUG(
"AssetManager: create new material {}",
id);
110tenno::shared_ptr<Font>
111AssetManager::new_asset<Font>(
const AssetId&
id,
114 auto ptr = tenno::make_shared<Font>(builder.build());
115 ptr.set_cache(
false);
116 AssetManager::fonts[id] = {builder, ptr};
118 auto watch_paths = builder.get_watch_paths();
119 for (
auto& watch_path : watch_paths)
121 AssetManager::hotreload_entries[watch_path] = {
122 .type = AssetType::Font,
127 DEBUG(
"AssetManager: create new font {}",
id);
133tenno::shared_ptr<Scene>
134AssetManager::new_asset<Scene>(
const AssetId&
id,
137 auto ptr = tenno::make_shared<Scene>(builder.build());
138 ptr.set_cache(
false);
139 AssetManager::scenes[id] = {builder, ptr};
141 DEBUG(
"AssetManager: create new scene {}",
id);
147tenno::shared_ptr<Shader>
148AssetManager::new_asset<Shader>(
const AssetId&
id,
151 auto maybe_shader = builder.build();
152 if (!maybe_shader)
return nullptr;
155 tenno::make_shared<Shader>(tenno::move(maybe_shader.value()));
156 shader.set_cache(
false);
157 AssetManager::shaders[id] = {builder, shader};
159 DEBUG(
"AssetManager: create new shader {}",
id);
165tenno::shared_ptr<SoundAsset>
166AssetManager::new_asset<SoundAsset>(
const AssetId&
id,
169 auto maybe_sound_asset = builder.build();
170 if (!maybe_sound_asset)
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};
177 DEBUG(
"AssetManager: create new sound {}",
id);
183tenno::shared_ptr<Texture> AssetManager::get<Texture>(
const AssetId&
id)
185 if (!AssetManager::textures.contains(
id))
return nullptr;
187 if (tenno::shared_ptr<Texture> ptr =
188 AssetManager::textures[
id].ptr.lock())
195tenno::shared_ptr<Model> AssetManager::get<Model>(
const AssetId&
id)
197 if (!AssetManager::models.contains(
id))
return nullptr;
199 if (tenno::shared_ptr<Model> ptr =
200 AssetManager::models[
id].ptr.lock())
207tenno::shared_ptr<Shader> AssetManager::get<Shader>(
const AssetId&
id)
209 if (!AssetManager::shaders.contains(
id))
return nullptr;
211 if (tenno::shared_ptr<Shader> ptr =
212 AssetManager::shaders[
id].ptr.lock())
219tenno::shared_ptr<Material> AssetManager::get<Material>(
const AssetId&
id)
221 if (!AssetManager::materials.contains(
id))
return nullptr;
223 if (tenno::shared_ptr<Material> ptr =
224 AssetManager::materials[
id].ptr.lock())
231tenno::shared_ptr<Scene> AssetManager::get<Scene>(
const AssetId&
id)
233 if (!AssetManager::scenes.contains(
id))
return nullptr;
234 return AssetManager::scenes[id].ptr;
238tenno::shared_ptr<Font> AssetManager::get<Font>(
const AssetId&
id)
240 if (!AssetManager::fonts.contains(
id))
return nullptr;
242 if (tenno::shared_ptr<Font> ptr =
243 AssetManager::fonts[
id].ptr.lock())
250tenno::shared_ptr<SoundAsset>
251AssetManager::get<SoundAsset>(
const AssetId&
id)
253 if (!AssetManager::sound_assets.contains(
id))
return nullptr;
254 return AssetManager::sound_assets[id].ptr;
258bool AssetManager::reload<Model>(
const AssetId&
id)
260 if (!AssetManager::models.contains(
id))
return false;
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);
267 DEBUG(
"AssetManager: reloaded model {}",
id);
273bool AssetManager::reload<Texture>(
const AssetId&
id)
275 if (!AssetManager::textures.contains(
id))
return false;
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);
282 DEBUG(
"AssetManager: reloaded texture {}",
id);
288bool AssetManager::reload<Material>(
const AssetId&
id)
290 if (!AssetManager::materials.contains(
id))
return false;
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);
297 DEBUG(
"AssetManager: reloaded material {}",
id);
303bool AssetManager::reload<Font>(
const AssetId&
id)
305 if (!AssetManager::fonts.contains(
id))
return false;
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);
312 DEBUG(
"AssetManager: reloaded font {}",
id);
318bool AssetManager::reload<Scene>(
const AssetId&
id)
320 if (!AssetManager::scenes.contains(
id))
return false;
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);
327 DEBUG(
"AssetManager: reloaded scene {}",
id);
333bool AssetManager::reload<Shader>(
const AssetId&
id)
335 if (!AssetManager::shaders.contains(
id))
return false;
337 Asset<Shader>& asset = AssetManager::shaders[id];
338 auto maybe_shader = asset.builder.build();
339 if (!maybe_shader)
return false;
341 tenno::shared_ptr<Shader> new_shader =
342 tenno::make_shared<Shader>(tenno::move(*new_shader));
343 asset.ptr.swap_ptr(new_shader);
345 DEBUG(
"AssetManager: reloaded shader {}",
id);
351bool AssetManager::reload<SoundAsset>(
const AssetId&
id)
353 if (!AssetManager::sound_assets.contains(
id))
return false;
355 AssetOwned<SoundAsset>& asset = AssetManager::sound_assets[id];
356 auto maybe_sound_asset = asset.builder.build();
357 if (!maybe_sound_asset)
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);
364 DEBUG(
"AssetManager: reloaded sound {}",
id);
369void AssetManager::clear()
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();
382void AssetManager::hotreload_activate()
384 if (AssetManager::hotreload_active)
return;
386 AssetManager::hotreload_active =
true;
388 AssetManager::hotreload_thread = tenno::jthread([](){
389 DEBUG(
"AssetManager: created hotreloading thread");
391 AssetManager::fswatcher.init();
395 AssetManager::fswatcher.add(path, {FilesystemWatcher::Event::Modify});
396 DEBUG(
"AssetManager: hotreloading setup {}", path.string());
399 while(AssetManager::hotreload_active)
401 auto maybe_event = AssetManager::fswatcher.watch();
402 if (!maybe_event)
continue;
404 DEBUG(
"AssetManager: hotreloading received event");
406 auto event = *maybe_event;
407 if (AssetManager::hotreload_entries.contains(event))
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());
418 DEBUG(
"AssetManager: hotreloading activated");
421void AssetManager::hotreload_deactivate()
423 if (!AssetManager::hotreload_active)
return;
425 AssetManager::hotreload_active =
false;
427 AssetManager::hotreload_thread.request_stop();
428 AssetManager::fswatcher.destroy();
430 DEBUG(
"AssetManager: hotreloading deactivated");
433void AssetManager::hotreload_update()
435 if (!AssetManager::hotreload_active)
return;
437 tenno::lock_guard<tenno::mutex> lock(AssetManager::hotreload_pending_mutex);
441 DEBUG(
"AssetManager: hotreloading now {}", item.id);
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;
452 AssetManager::hotreload_pending.clear();