Textures and palette moved to graphics.

This commit is contained in:
Zed A. Shaw 2026-02-27 13:09:44 -05:00
parent b91e9ffaf6
commit 229ad2dd95
30 changed files with 31 additions and 31 deletions

72
src/graphics/palette.cpp Normal file
View file

@ -0,0 +1,72 @@
#include <fmt/core.h>
#include "graphics/palette.hpp"
#include "config.hpp"
#include "dbc.hpp"
namespace palette {
using std::string;
using nlohmann::json;
struct PaletteMgr {
std::unordered_map<string, sf::Color> palettes;
std::string config;
std::unordered_map<string, string> pending_refs;
bool initialized = false;
};
static PaletteMgr COLOR;
bool initialized() {
return COLOR.initialized;
}
void init(const string &json_file) {
if(!COLOR.initialized) {
COLOR.initialized = true;
COLOR.config = json_file;
auto config = settings::get(json_file);
json& colors = config.json();
for(auto [key, value_specs] : colors.items()) {
const string& base_key = key;
for(auto [value, rgba] : value_specs.items()) {
auto color_path = base_key + ":" + value;
dbc::check(!COLOR.palettes.contains(color_path),
fmt::format("PALLETES config {} already has a color path {}", COLOR.config, color_path));
if(rgba.type() == json::value_t::string) {
COLOR.pending_refs.try_emplace(color_path, rgba);
} else {
uint8_t alpha = rgba.size() == 3 ? 255 : (uint8_t)rgba[3];
sf::Color color{rgba[0], rgba[1], rgba[2], alpha};
COLOR.palettes.try_emplace(color_path, color);
}
}
}
for(auto [color_path, ref] : COLOR.pending_refs) {
dbc::check(COLOR.palettes.contains(ref),
fmt::format("In {} you have {} referring to {} but {} doesn't exist.",
COLOR.config, color_path, ref, ref));
dbc::check(!COLOR.palettes.contains(color_path),
fmt::format("Color {} with ref {} is duplicated.", color_path, ref));
auto color = COLOR.palettes.at(ref);
COLOR.palettes.try_emplace(color_path, color);
}
}
}
sf::Color get(const string& key) {
dbc::check(COLOR.palettes.contains(key),
fmt::format("COLOR {} is missing from {}", key, COLOR.config));
return COLOR.palettes.at(key);
}
sf::Color get(const string& key, const string& value) {
return get(key + ":" + value);
}
}

13
src/graphics/palette.hpp Normal file
View file

@ -0,0 +1,13 @@
#include <string>
#include <SFML/Graphics/Color.hpp>
namespace palette {
using std::string;
bool initialized();
void init(const std::string &config="palette");
sf::Color get(const string &key);
sf::Color get(const string &key, const string &value);
}

203
src/graphics/textures.cpp Normal file
View file

@ -0,0 +1,203 @@
#include "graphics/textures.hpp"
#include <SFML/Graphics/Image.hpp>
#include "dbc.hpp"
#include <fmt/core.h>
#include "config.hpp"
#include "constants.hpp"
#include <memory>
#include <filesystem>
namespace textures {
using std::shared_ptr, std::make_shared, nlohmann::json, std::string;
namespace fs = std::filesystem;
static TextureManager TMGR;
static bool initialized = false;
static bool failure = false;
void load_sprite_textures(SpriteTextureMap &mapping, json &config, bool smooth) {
for(auto& [name, settings] : config.items()) {
const string& path = settings["path"];
dbc::check(fs::exists(path), fmt::format("texture at {} doesn't exist", path));
auto texture = make_shared<sf::Texture>(path);
texture->setSmooth(smooth);
auto sprite = make_shared<sf::Sprite>(*texture);
int width = settings["frame_width"];
int height = settings["frame_height"];
dbc::check(width % 2 == 0 && height % 2 == 0,
fmt::format("sprite {}:{} has invalid frame size {}:{}",
path, name, width, height));
sf::Vector2i frame_size{width, height};
sprite->setTextureRect({{0,0}, frame_size});
dbc::check(!mapping.contains(name),
fmt::format("duplicate sprite/icon name {}", (string)name));
mapping.try_emplace(name, sprite, texture, frame_size);
}
}
void load_sprites() {
auto sprites = settings::get("config");
bool smooth = sprites["graphics"]["smooth_textures"];
load_sprite_textures(TMGR.sprite_textures, sprites["sprites"], smooth);
auto icons = settings::get("assets/icons.json");
load_sprite_textures(TMGR.icon_textures, icons.json(), smooth);
}
inline void resize_shit(size_t size) {
TMGR.surfaces.resize(size);
TMGR.ceilings.resize(size);
TMGR.map_tile_set.resize(size);
TMGR.ambient_light.resize(size);
}
void load_tiles() {
auto assets = settings::get("tiles");
auto &tiles = assets.json();
resize_shit(tiles.size());
for(auto &el : tiles.items()) {
auto &config = el.value();
const string& texture_fname = config["texture"];
size_t surface_i = config["id"];
dbc::check(!TMGR.name_to_id.contains(el.key()),
fmt::format("duplicate key in textures {}",
(string)el.key()));
TMGR.name_to_id.insert_or_assign(el.key(), surface_i);
if(surface_i >= tiles.size()) {
resize_shit(surface_i + 1);
}
TMGR.map_tile_set[surface_i] = config["display"];
TMGR.ambient_light[surface_i] = config["light"];
TMGR.surfaces[surface_i] = load_image(texture_fname);
// NOTE: ceilings defaults to 0 which is floor texture so only need to update
if(config.contains("ceiling")) {
const string& name = config["ceiling"];
dbc::check(tiles.contains(name), fmt::format("invalid ceiling name {} in tile config {}", name, (string)el.key()));
auto& ceiling = tiles[name];
TMGR.ceilings[surface_i] = ceiling["id"];
}
}
}
void load_map_tiles() {
auto config = settings::get("map_tiles");
json& tiles = config.json();
for(auto tile : tiles) {
sf::Vector2i coords{tile["x"], tile["y"]};
dbc::check(coords.x % ICONGEN_MAP_TILE_DIM == 0, "x coordinates wrong in map");
dbc::check(coords.y % ICONGEN_MAP_TILE_DIM == 0, "y coordinates wrong in map");
sf::IntRect square{coords, {ICONGEN_MAP_TILE_DIM, ICONGEN_MAP_TILE_DIM}};
sf::Sprite sprite{TMGR.map_sprite_sheet, square};
wchar_t display = tile["display"];
dbc::check(!TMGR.map_sprites.contains(display),
fmt::format("duplicate tile display {} in map_tiles.json", int(display)));
TMGR.map_sprites.try_emplace(display, sprite);
}
}
void init() {
dbc::check(!failure, "YOU HAD A CATASTROPHIC TEXTURES FAILURE, FIX IT");
try {
if(!initialized) {
load_tiles();
load_sprites();
load_map_tiles();
initialized = true;
}
} catch(...) {
failure = true;
throw;
}
}
SpriteTexture& get(const string& name, SpriteTextureMap& mapping) {
dbc::check(initialized, "you forgot to call textures::init()");
dbc::check(mapping.contains(name),
fmt::format("!!!!! textures do not contain {} sprite", name));
auto& result = mapping.at(name);
dbc::check(result.sprite != nullptr,
fmt::format("bad sprite from textures::get named {}", name));
dbc::check(result.texture != nullptr,
fmt::format("bad texture from textures::get named {}", name));
return result;
}
SpriteTexture get_sprite(const string& name, bool duped) {
auto& st = get(name, TMGR.sprite_textures);
if(duped) {
st.sprite = make_shared<sf::Sprite>(*st.sprite);
}
return st;
}
SpriteTexture get_icon(const string& name) {
return get(name, TMGR.icon_textures);
}
sf::Image load_image(const string& filename) {
sf::Image texture;
bool good = texture.loadFromFile(filename);
dbc::check(good, fmt::format("failed to load {}", filename));
return texture;
}
std::vector<int>& get_ambient_light() {
return TMGR.ambient_light;
}
std::vector<wchar_t>& get_map_tile_set() {
return TMGR.map_tile_set;
}
const uint32_t* get_surface(size_t num) {
return (const uint32_t *)TMGR.surfaces[num].getPixelsPtr();
}
sf::Image& get_surface_img(size_t num) {
return TMGR.surfaces[num];
}
const uint32_t* get_ceiling(size_t num) {
size_t ceiling_num = TMGR.ceilings[num];
return (const uint32_t *)TMGR.surfaces[ceiling_num].getPixelsPtr();
}
size_t get_id(const string& name) {
dbc::check(TMGR.name_to_id.contains(name),
fmt::format("there is no texture named {} in tiles.json", name));
return TMGR.name_to_id.at(name);
}
sf::Sprite& get_map_sprite(wchar_t display) {
dbc::check(TMGR.map_sprites.contains(display),
fmt::format("map_sprites.json doesn't have {} sprite", int(display)));
return TMGR.map_sprites.at(display);
}
};

54
src/graphics/textures.hpp Normal file
View file

@ -0,0 +1,54 @@
#pragma once
#include <cstdint>
#include <vector>
#include <string>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <unordered_map>
#include <memory>
#include "algos/matrix.hpp"
namespace textures {
struct SpriteTexture {
std::shared_ptr<sf::Sprite> sprite = nullptr;
std::shared_ptr<sf::Texture> texture = nullptr;
sf::Vector2i frame_size;
};
using SpriteTextureMap = std::unordered_map<std::string, SpriteTexture>;
struct TextureManager {
std::vector<sf::Image> surfaces;
std::vector<size_t> ceilings;
std::vector<wchar_t> map_tile_set;
std::vector<int> ambient_light;
SpriteTextureMap sprite_textures;
SpriteTextureMap icon_textures;
std::unordered_map<std::string, size_t> name_to_id;
std::unordered_map<wchar_t, sf::Sprite> map_sprites;
sf::Texture map_sprite_sheet{"./assets/map_tiles.png"};
};
void init();
SpriteTexture get_sprite(const std::string& name, bool duped=false);
SpriteTexture get_icon(const std::string& name);
sf::Image load_image(const std::string& filename);
std::vector<int>& get_ambient_light();
std::vector<wchar_t>& get_map_tile_set();
const uint32_t* get_surface(size_t num);
sf::Image& get_surface_img(size_t num);
const uint32_t* get_ceiling(size_t num);
sf::Sprite& get_map_sprite(wchar_t display);
size_t get_id(const std::string& name);
}