// // manager.cxx // graphics // // Created by Sam Jaffe on 5/25/19. // Copyright © 2019 Sam Jaffe. All rights reserved. // #include "game/graphics/manager.hpp" #include "game/graphics/exception.h" #include "game/graphics/material.hpp" #include "game/graphics/object.hpp" #include "game/graphics/shader.hpp" #include "game/graphics/shader_program.hpp" #include "game/graphics/texture.hpp" #include "game/math/shape.hpp" #include "game/util/hash.hpp" #include "helper.hpp" using namespace graphics; template struct key; template using key_t = typename key::type; template <> struct key { using type = std::string; }; template <> struct key { using type = std::pair; }; template <> struct key { using type = std::pair; }; template <> struct key { using type = std::tuple, std::string, std::string>; }; template struct cache { identity emplace(key_t const & key, T && value); std::unordered_map, key_t> keys; std::unordered_map, T> values; }; template identity cache::emplace(key_t const & key, T && value) { keys.emplace(value, key); return values.emplace(key, std::forward(value)).first->second; } struct graphics::manager_cache { cache materials; cache shaders; cache programs; cache textures; }; manager::manager() : pcache_(new manager_cache) {} manager::~manager() {} materials::uniform uniform_id(std::string const & uniform) { if (uniform == "u_normalMap") { return materials::uniform::NORMAL; } else if (uniform == "u_specularMap") { return materials::uniform::SPECULAR; } else if (uniform == "u_diffuseMap") { return materials::uniform::DIFFUSE; } throw unmapped_enum(uniform); } identity manager::get(identity program, std::string const & texture, std::string const & uniform) const { auto key = std::make_tuple(program, texture, uniform); auto & cache = pcache_->materials; auto found = cache.values.find(key); if (found != cache.values.end()) { return found->second; } std::vector uniforms; uniforms.push_back( {texture_or_uniform(texture, uniform), uniform_id(uniform)}); math::vec2i size = get(uniforms[0].texture).size; // if (!uniforms.empty()) { size = ...; } return cache.emplace(key, material(program, size, uniforms)); } identity manager::get(shaders::type type, std::string const & path) const { auto key = std::make_pair(type, path); auto & cache = pcache_->shaders; auto found = cache.values.find(key); if (found != cache.values.end()) { return found->second; } return cache.emplace(key, compile(type, path)); } identity manager::get(std::string const & fragment, std::string const & vertex) const { auto key = std::make_pair(fragment, vertex); auto & cache = pcache_->programs; auto found = cache.values.find(key); if (found != cache.values.end()) { return found->second; } auto fragment_shader = get(shaders::type::FRAGMENT, fragment); auto vertex_shader = get(shaders::type::VERTEX, vertex); return cache.emplace(key, compile(fragment_shader, vertex_shader)); } identity manager::get(std::string const & path) const { auto & cache = pcache_->textures; auto found = cache.values.find(path); if (found != cache.values.end()) { return found->second; } textures::external_data data(path); return cache.emplace(path, compile(data.color, data.size, data.buffer)); } object manager::create_object(identity fromMaterial, math::vec2 atPosition, math::vec2 frameWidth, float scale) const { math::dim2::rectangle bounds{atPosition, frameWidth * get(fromMaterial).size * (scale ? scale : 1.f)}; return {bounds, bounds, fromMaterial, {make_vector(0.f, 0.f), frameWidth}}; } material const & manager::get(identity identity) const { auto & cache = pcache_->materials; return cache.values.find(cache.keys.find(identity)->second)->second; } texture const & manager::get(identity identity) const { auto & cache = pcache_->textures; auto it = cache.keys.find(identity); // if (it == cache.keys.end()) { return texture::WHITE(); } return cache.values.find(it->second)->second; } void manager::prepare_uniforms() const { // Initialize the three default uniform-textures immediately auto & cache = pcache_->textures; if (cache.values.count("u_normalMap") != 0) return; auto RGBA = textures::format::RGBA; cache.emplace("u_normalMap", compile(RGBA, {{1, 1}}, "\x80\x80\xFF\xFF")); cache.emplace("u_specularMap", compile(RGBA, {{1, 1}}, "\x80\x80\x00\xFF")); cache.emplace("u_diffuseMap", compile(RGBA, {{1, 1}}, "\xFF\xFF\xFF\xFF")); } identity manager::texture_or_uniform(std::string const & path, std::string const & uniform) const { if (!path.empty()) { try { return get(path); } catch (std::exception const & e) { // TODO: Logging } } prepare_uniforms(); // The uniform is primed into the cache already. auto & cache = pcache_->textures; return cache.values.find(uniform)->second; }