351 lines
13 KiB
C++
351 lines
13 KiB
C++
// src/ResourceManager.cpp
|
|
|
|
#include "ResourceManager.h"
|
|
#include "Shader.h"
|
|
#include "Mesh.h"
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <unordered_set>
|
|
#include <algorithm>
|
|
|
|
// Static member definitions
|
|
std::unordered_map<std::string, std::shared_ptr<Shader>> ResourceManager::shaders_;
|
|
std::unordered_map<std::string, std::shared_ptr<Texture>> ResourceManager::textures_;
|
|
std::unordered_map<std::string, std::shared_ptr<Mesh>> ResourceManager::meshes_;
|
|
bool ResourceManager::hot_reload_enabled_ = false;
|
|
std::unordered_map<std::string, std::filesystem::file_time_type> ResourceManager::shader_timestamps_;
|
|
std::unordered_map<std::string, std::filesystem::path> ResourceManager::shader_paths_;
|
|
std::unordered_map<std::string, std::filesystem::file_time_type> ResourceManager::texture_timestamps_;
|
|
std::unordered_map<std::string, std::filesystem::path> ResourceManager::texture_paths_;
|
|
|
|
std::expected<std::shared_ptr<Shader>, std::string>
|
|
ResourceManager::LoadShader(const std::string& name,
|
|
const std::filesystem::path& vert,
|
|
const std::filesystem::path& frag) {
|
|
try {
|
|
auto shader = std::make_shared<Shader>(vert, frag);
|
|
if (!shader->IsValid()) {
|
|
return std::unexpected("Shader compilation/linking failed: " + name);
|
|
}
|
|
shaders_[name] = shader;
|
|
if (hot_reload_enabled_) {
|
|
shader_timestamps_[name] = std::filesystem::last_write_time(vert);
|
|
shader_paths_[name] = vert;
|
|
}
|
|
return shader;
|
|
} catch (const std::exception& e) {
|
|
return std::unexpected(std::string("Exception loading shader ") + name + ": " + e.what());
|
|
}
|
|
}
|
|
|
|
std::expected<std::shared_ptr<Shader>, std::string>
|
|
ResourceManager::LoadShader(const std::string& name,
|
|
const std::filesystem::path& vert,
|
|
const std::filesystem::path& geom,
|
|
const std::filesystem::path& frag) {
|
|
try {
|
|
auto shader = std::make_shared<Shader>(vert, geom, frag);
|
|
if (!shader->IsValid()) {
|
|
return std::unexpected("Shader compilation/linking failed: " + name);
|
|
}
|
|
shaders_[name] = shader;
|
|
if (hot_reload_enabled_) {
|
|
shader_timestamps_[name] = std::filesystem::last_write_time(vert);
|
|
shader_paths_[name] = vert;
|
|
}
|
|
return shader;
|
|
} catch (const std::exception& e) {
|
|
return std::unexpected(std::string("Exception loading shader ") + name + ": " + e.what());
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Shader> ResourceManager::GetShader(const std::string& name) {
|
|
auto it = shaders_.find(name);
|
|
return (it != shaders_.end()) ? it->second : nullptr;
|
|
}
|
|
|
|
std::expected<std::shared_ptr<Texture>, std::string>
|
|
ResourceManager::LoadTexture(const std::string& name,
|
|
const std::filesystem::path& path,
|
|
bool generate_mipmaps) {
|
|
try {
|
|
// TODO: Replace this stub with actual texture loading
|
|
// auto texture = std::make_shared<Texture>(path, generate_mipmaps);
|
|
// if (!texture->IsValid()) {
|
|
// return std::unexpected("Texture loading failed: " + name);
|
|
// }
|
|
// textures_[name] = texture;
|
|
|
|
if (hot_reload_enabled_) {
|
|
texture_timestamps_[name] = std::filesystem::last_write_time(path);
|
|
texture_paths_[name] = path;
|
|
}
|
|
|
|
// For now, return error since texture loading is not implemented
|
|
return std::unexpected("Texture loading not implemented");
|
|
} catch (const std::exception& e) {
|
|
return std::unexpected(std::string("Exception loading texture ") + name + ": " + e.what());
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Texture> ResourceManager::GetTexture(const std::string& name) {
|
|
auto it = textures_.find(name);
|
|
return (it != textures_.end()) ? it->second : nullptr;
|
|
}
|
|
|
|
void ResourceManager::RegisterMesh(const std::string& name, std::shared_ptr<Mesh> mesh) {
|
|
meshes_[name] = std::move(mesh);
|
|
}
|
|
|
|
std::shared_ptr<Mesh> ResourceManager::GetMesh(const std::string& name) {
|
|
auto it = meshes_.find(name);
|
|
return (it != meshes_.end()) ? it->second : nullptr;
|
|
}
|
|
|
|
void ResourceManager::ClearShaders() {
|
|
shaders_.clear();
|
|
shader_timestamps_.clear();
|
|
shader_paths_.clear();
|
|
}
|
|
|
|
void ResourceManager::ClearTextures() {
|
|
textures_.clear();
|
|
texture_timestamps_.clear();
|
|
texture_paths_.clear();
|
|
}
|
|
|
|
void ResourceManager::ClearMeshes() {
|
|
meshes_.clear();
|
|
}
|
|
|
|
void ResourceManager::ClearAll() {
|
|
ClearShaders();
|
|
ClearTextures();
|
|
ClearMeshes();
|
|
}
|
|
|
|
void ResourceManager::PrintResourceInfo() {
|
|
std::cout << "Shaders: " << shaders_.size()
|
|
<< ", Textures: " << textures_.size()
|
|
<< ", Meshes: " << meshes_.size() << "\n";
|
|
}
|
|
|
|
bool ResourceManager::HasShader(const std::string& name) {
|
|
return shaders_.count(name) > 0;
|
|
}
|
|
|
|
bool ResourceManager::HasTexture(const std::string& name) {
|
|
return textures_.count(name) > 0;
|
|
}
|
|
|
|
bool ResourceManager::HasMesh(const std::string& name) {
|
|
return meshes_.count(name) > 0;
|
|
}
|
|
|
|
void ResourceManager::LoadShadersFromDirectory(const std::filesystem::path& dir) {
|
|
if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) {
|
|
std::cerr << "Directory does not exist: " << dir << "\n";
|
|
return;
|
|
}
|
|
|
|
std::unordered_map<std::string, std::filesystem::path> vert_files;
|
|
std::unordered_map<std::string, std::filesystem::path> frag_files;
|
|
std::unordered_map<std::string, std::filesystem::path> geom_files;
|
|
|
|
// Collect shader files by base name
|
|
for (const auto& entry : std::filesystem::directory_iterator(dir)) {
|
|
if (!entry.is_regular_file()) continue;
|
|
|
|
auto path = entry.path();
|
|
auto base_name = path.stem().string();
|
|
auto extension = path.extension().string();
|
|
|
|
if (extension == ".vert") {
|
|
vert_files[base_name] = path;
|
|
} else if (extension == ".frag") {
|
|
frag_files[base_name] = path;
|
|
} else if (extension == ".geom") {
|
|
geom_files[base_name] = path;
|
|
}
|
|
}
|
|
|
|
// Match and load shader pairs/triplets
|
|
for (const auto& [base_name, vert_path] : vert_files) {
|
|
auto frag_it = frag_files.find(base_name);
|
|
if (frag_it != frag_files.end()) {
|
|
auto geom_it = geom_files.find(base_name);
|
|
|
|
if (geom_it != geom_files.end()) {
|
|
// Load with geometry shader
|
|
auto result = LoadShader(base_name, vert_path, geom_it->second, frag_it->second);
|
|
if (result) {
|
|
std::cout << "Loaded shader with geometry: " << base_name << "\n";
|
|
} else {
|
|
std::cerr << "Failed to load shader " << base_name << ": " << result.error() << "\n";
|
|
}
|
|
} else {
|
|
// Load without geometry shader
|
|
auto result = LoadShader(base_name, vert_path, frag_it->second);
|
|
if (result) {
|
|
std::cout << "Loaded shader: " << base_name << "\n";
|
|
} else {
|
|
std::cerr << "Failed to load shader " << base_name << ": " << result.error() << "\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ResourceManager::LoadTexturesFromDirectory(const std::filesystem::path& dir) {
|
|
if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) {
|
|
std::cerr << "Directory does not exist: " << dir << "\n";
|
|
return;
|
|
}
|
|
|
|
// Common image extensions using unordered_set instead of set
|
|
std::unordered_set<std::string> image_extensions = {".png", ".jpg", ".jpeg", ".bmp", ".tga", ".dds"};
|
|
|
|
for (const auto& entry : std::filesystem::directory_iterator(dir)) {
|
|
if (!entry.is_regular_file()) continue;
|
|
|
|
auto path = entry.path();
|
|
auto extension = path.extension().string();
|
|
|
|
// Convert to lowercase for comparison
|
|
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
|
|
|
|
if (image_extensions.count(extension)) {
|
|
auto name = path.stem().string();
|
|
auto result = LoadTexture(name, path);
|
|
if (result) {
|
|
std::cout << "Loaded texture: " << name << "\n";
|
|
} else {
|
|
std::cerr << "Failed to load texture " << name << ": " << result.error() << "\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ResourceManager::ReloadShader(const std::string& name) {
|
|
auto shader_it = shaders_.find(name);
|
|
if (shader_it == shaders_.end()) {
|
|
std::cerr << "Cannot reload shader: " << name << " not found\n";
|
|
return;
|
|
}
|
|
|
|
auto path_it = shader_paths_.find(name);
|
|
if (path_it == shader_paths_.end()) {
|
|
std::cerr << "Cannot reload shader: " << name << " path not stored\n";
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Determine fragment shader path (assume same base name, different extension)
|
|
auto vert_path = path_it->second;
|
|
auto frag_path = vert_path;
|
|
frag_path.replace_extension(".frag");
|
|
|
|
// Check if geometry shader exists
|
|
auto geom_path = vert_path;
|
|
geom_path.replace_extension(".geom");
|
|
|
|
if (std::filesystem::exists(geom_path)) {
|
|
// Reload with geometry shader
|
|
auto new_shader = std::make_shared<Shader>(vert_path, geom_path, frag_path);
|
|
if (new_shader->IsValid()) {
|
|
shaders_[name] = new_shader;
|
|
shader_timestamps_[name] = std::filesystem::last_write_time(vert_path);
|
|
std::cout << "Reloaded shader with geometry: " << name << "\n";
|
|
} else {
|
|
std::cerr << "Failed to reload shader with geometry: " << name << "\n";
|
|
}
|
|
} else {
|
|
// Reload without geometry shader
|
|
auto new_shader = std::make_shared<Shader>(vert_path, frag_path);
|
|
if (new_shader->IsValid()) {
|
|
shaders_[name] = new_shader;
|
|
shader_timestamps_[name] = std::filesystem::last_write_time(vert_path);
|
|
std::cout << "Reloaded shader: " << name << "\n";
|
|
} else {
|
|
std::cerr << "Failed to reload shader: " << name << "\n";
|
|
}
|
|
}
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "Exception reloading shader " << name << ": " << e.what() << "\n";
|
|
}
|
|
}
|
|
|
|
void ResourceManager::ReloadTexture(const std::string& name) {
|
|
auto texture_it = textures_.find(name);
|
|
if (texture_it == textures_.end()) {
|
|
std::cerr << "Cannot reload texture: " << name << " not found\n";
|
|
return;
|
|
}
|
|
|
|
auto path_it = texture_paths_.find(name);
|
|
if (path_it == texture_paths_.end()) {
|
|
std::cerr << "Cannot reload texture: " << name << " path not stored\n";
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// TODO: Replace this with actual texture reloading
|
|
// auto new_texture = std::make_shared<Texture>(path_it->second);
|
|
// if (new_texture->IsValid()) {
|
|
// textures_[name] = new_texture;
|
|
// texture_timestamps_[name] = std::filesystem::last_write_time(path_it->second);
|
|
// std::cout << "Reloaded texture: " << name << "\n";
|
|
// } else {
|
|
// std::cerr << "Failed to reload texture: " << name << "\n";
|
|
// }
|
|
|
|
// For now, just update timestamp
|
|
texture_timestamps_[name] = std::filesystem::last_write_time(path_it->second);
|
|
std::cout << "Texture reload placeholder for: " << name << "\n";
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "Exception reloading texture " << name << ": " << e.what() << "\n";
|
|
}
|
|
}
|
|
|
|
void ResourceManager::CheckForChanges() {
|
|
if (!hot_reload_enabled_) return;
|
|
|
|
// Check for shader changes
|
|
for (const auto& [name, shader] : shaders_) {
|
|
auto path_it = shader_paths_.find(name);
|
|
if (path_it == shader_paths_.end()) continue;
|
|
|
|
try {
|
|
auto current = std::filesystem::last_write_time(path_it->second);
|
|
auto stored_it = shader_timestamps_.find(name);
|
|
|
|
if (stored_it != shader_timestamps_.end() && current != stored_it->second) {
|
|
std::cout << "Detected change in shader: " << name << "\n";
|
|
ReloadShader(name);
|
|
}
|
|
} catch (const std::filesystem::filesystem_error& e) {
|
|
std::cerr << "Error checking shader file " << name << ": " << e.what() << "\n";
|
|
}
|
|
}
|
|
|
|
// Check for texture changes
|
|
for (const auto& [name, texture] : textures_) {
|
|
auto path_it = texture_paths_.find(name);
|
|
if (path_it == texture_paths_.end()) continue;
|
|
|
|
try {
|
|
auto current = std::filesystem::last_write_time(path_it->second);
|
|
auto stored_it = texture_timestamps_.find(name);
|
|
|
|
if (stored_it != texture_timestamps_.end() && current != stored_it->second) {
|
|
std::cout << "Detected change in texture: " << name << "\n";
|
|
ReloadTexture(name);
|
|
texture_timestamps_[name] = current;
|
|
}
|
|
} catch (const std::filesystem::filesystem_error& e) {
|
|
std::cerr << "Error checking texture file " << name << ": " << e.what() << "\n";
|
|
}
|
|
}
|
|
}
|