// src/ResourceManager.cpp #include "ResourceManager.h" #include "Shader.h" #include "Mesh.h" #include #include #include #include #include // Static member definitions std::unordered_map> ResourceManager::shaders_; std::unordered_map> ResourceManager::textures_; std::unordered_map> ResourceManager::meshes_; bool ResourceManager::hot_reload_enabled_ = false; std::unordered_map ResourceManager::shader_timestamps_; std::unordered_map ResourceManager::shader_paths_; std::unordered_map ResourceManager::texture_timestamps_; std::unordered_map ResourceManager::texture_paths_; std::expected, std::string> ResourceManager::LoadShader(const std::string& name, const std::filesystem::path& vert, const std::filesystem::path& frag) { try { auto shader = std::make_shared(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::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(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 ResourceManager::GetShader(const std::string& name) { auto it = shaders_.find(name); return (it != shaders_.end()) ? it->second : nullptr; } std::expected, 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(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 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) { meshes_[name] = std::move(mesh); } std::shared_ptr 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 vert_files; std::unordered_map frag_files; std::unordered_map 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 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(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(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(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"; } } }