graphics: begin to rewrite code to be more simple: - add

a struct Texture to store a sf::Texture and the application's texture sizes,
 - add a texture resources manager and retrieve codes to allow - if needed,
 rescaling startsplash and spidlogo at startup - loading some graphics with
 different resolutions:     buttons, inventory, pcedbuttons, statarea,
 startanim,     startbut, startup, terscreen, textbar, transcript   can be
 used to load high resolution pictures.

Note: currently, render_image and res_image are a mess as Texture and sf:Texture classes
  cohabit :-~
This commit is contained in:
Laurent alonso
2020-10-15 19:37:38 +02:00
committed by Celtic Minstrel
parent 44c063abd2
commit e625927a29
15 changed files with 226 additions and 39 deletions

View File

@@ -31,7 +31,7 @@ std::filebuf logfile;
static fs::path get_posix_tempdir();
static void add_resmgr_paths(const fs::path& basePath) {
ResMgr::graphics.pushPath(basePath/"graphics");
ResMgr::textures.pushPath(basePath/"graphics");
ResMgr::cursors.pushPath(basePath/"cursors");
ResMgr::fonts.pushPath(basePath/"fonts");
ResMgr::strings.pushPath(basePath/"strings");

View File

@@ -101,10 +101,10 @@ fs::path locate_scenario(std::string scen_name) {
bool load_scenario(fs::path file_to_load, cScenario& scenario, bool only_header) {
// Before loading a scenario, we may need to pop scenario resource paths.
fs::path graphics_path = ResMgr::graphics.popPath();
fs::path graphics_path = ResMgr::textures.popPath();
for(auto p : graphics_path) {
if(p.string() == "data") {
ResMgr::graphics.pushPath(graphics_path);
ResMgr::textures.pushPath(graphics_path);
break;
}
}
@@ -2199,11 +2199,11 @@ bool load_scenario_v2(fs::path file_to_load, cScenario& scenario, bool only_head
// This is a bit of trickery to get it to only count the first consecutive range of sheets
while(have_pic[num_graphic_sheets])
num_graphic_sheets++;
ResMgr::graphics.pushPath(tempDir/scenario_temp_dir_name/"graphics");
ResMgr::textures.pushPath(tempDir/scenario_temp_dir_name/"graphics");
ResMgr::sounds.pushPath(tempDir/scenario_temp_dir_name/"sounds");
} else {
if(fs::is_directory(file_to_load/"graphics"))
ResMgr::graphics.pushPath(file_to_load/"graphics");
ResMgr::textures.pushPath(file_to_load/"graphics");
if(fs::is_directory(file_to_load/"sounds"))
ResMgr::sounds.pushPath(file_to_load/"sounds");
std::string fname;

View File

@@ -8,6 +8,8 @@
#include "res_image.hpp"
#include "texture.hpp"
class ImageLoader : public ResMgr::cLoader<sf::Texture> {
/// Load an image from a PNG file.
sf::Texture* operator() (const fs::path& fpath) const override {
@@ -29,3 +31,33 @@ class ImageLoader : public ResMgr::cLoader<sf::Texture> {
// TODO: What's a good max texture count?
static ImageLoader loader;
ResMgr::cPool<sf::Texture> ResMgr::graphics(loader, 50);
class TextureLoader : public ResMgr::cLoader<Texture> {
/// Load an image from a PNG file.
Texture* operator() (const fs::path& fpath) const override {
auto img=std::make_shared<sf::Texture>();
if(img->loadFromFile(fpath.string())) {
Texture *texture=new Texture;
texture->texture=img;
texture->dimension=Texture::getApplicationDimension(fpath.filename().string());
if (texture->dimension.x==0 || texture->dimension.y==0)
texture->dimension=sf::Vector2u(img->getSize());
return texture;
}
throw ResMgr::xError(ResMgr::ERR_LOAD, "Failed to load PNG image: " + fpath.string());
}
ResourceList expand(const std::string& name) const override {
return {name + ".png", name + ".bmp"};
}
std::string typeName() const override {
return "image";
}
};
// TODO: What's a good max texture count?
static TextureLoader textLoader;
ResMgr::cPoolTexture ResMgr::textures(textLoader, 50);

View File

@@ -11,11 +11,30 @@
#include <SFML/Graphics.hpp>
#include "resmgr.hpp"
#include "texture.hpp"
using ImageRsrc = ResMgr::cPointer<sf::Texture>;
using TextureRsrc = ResMgr::cPointer<Texture>;
namespace ResMgr {
extern cPool<sf::Texture> graphics;
// temporary while graphics is not suppressed
class cPoolTexture : public cPool<Texture> {
public:
cPoolTexture(cLoader<Texture>& loader, size_t max, std::string dir = "")
: cPool<Texture>(loader, max, dir)
{}
fs::path popPath() {
graphics.popPath();
return cPool<Texture>::popPath();
}
void pushPath(const fs::path& path) {
cPool<Texture>::pushPath(path);
graphics.pushPath(path);
}
};
extern ResMgr::cPoolTexture textures;
}
#endif

View File

@@ -84,7 +84,7 @@ long anim_ticks = 0;
extern enum_map(eGuiArea, rectangle) win_to_rects;
// 0 - title 1 - button 2 - credits 3 - base button
rectangle startup_from[4] = {{0,0,274,602},{274,0,322,301},{0,301,67,579},{274,301,314,341}};
rectangle const startup_from[4] = {{0,0,274,602},{274,0,322,301},{0,301,67,579},{274,301,314,341}};
extern enum_map(eStartButton, rectangle) startup_button;
rectangle top_left_rec = {0,0,36,28};
@@ -96,13 +96,15 @@ short debug_nums[6] = {0,0,0,0,0,0};
char light_area[13][13];
char unexplored_area[13][13];
// Declare the graphics
// Declare the graphics and their dimension
sf::RenderTexture pc_stats_gworld;
sf::RenderTexture item_stats_gworld;
sf::RenderTexture text_area_gworld;
sf::RenderTexture terrain_screen_gworld;
sf::RenderTexture text_bar_gworld;
sf::RenderTexture map_gworld;
rectangle terrain_screen_rect;
rectangle text_area_rect;
bool has_run_anim = false,currently_loading_graphics = false;
@@ -243,13 +245,13 @@ sf::FloatRect compute_viewport(const sf::RenderWindow& mainPtr, int mode, float
void init_startup() {
// Preload the main startup images
ResMgr::graphics.get("startup", true);
ResMgr::graphics.get("startbut", true);
ResMgr::graphics.get("startanim", true);
ResMgr::textures.get("startup", true);
ResMgr::textures.get("startbut", true);
ResMgr::textures.get("startanim", true);
}
void draw_startup(short but_type) {
sf::Texture& startup_gworld = *ResMgr::graphics.get("startup", true);
auto const &startup_gworld = *ResMgr::textures.get("startup", true);
rect_draw_some_item(startup_gworld,startup_from[0],mainPtr,startup_top);
for(auto btn : startup_button.keys()) {
@@ -269,9 +271,11 @@ void draw_startup_anim(bool advance) {
anim_from = anim_to;
anim_from.offset(-1,-4 + startup_anim_pos);
if(advance) startup_anim_pos = (startup_anim_pos + 1) % 542;
rect_draw_some_item(*ResMgr::graphics.get("startbut",true),anim_size,mainPtr,startup_button[STARTBTN_SCROLL]);
auto const &startbut=*ResMgr::textures.get("startbut",true);
rect_draw_some_item(startbut,startbut.dimension,mainPtr,startup_button[STARTBTN_SCROLL]);
anim_to.offset(startup_button[STARTBTN_SCROLL].left, startup_button[STARTBTN_SCROLL].top);
rect_draw_some_item(*ResMgr::graphics.get("startanim",true),anim_from,mainPtr,anim_to,sf::BlendAlpha);
auto const &startanim=*ResMgr::textures.get("startanim",true);
rect_draw_some_item(startanim,anim_from,mainPtr,anim_to,sf::BlendAlpha);
}
void draw_startup_stats() {
@@ -420,14 +424,14 @@ void draw_start_button(eStartButton which_position,short which_button) {
"Start Scenario","Custom Scenario","Quit"};
// The 0..65535 version of the blue component was 14472; the commented version was 43144431
sf::Color base_color = {0,0,57};
auto const &startup_gworld = *ResMgr::textures.get("startup",true);
from_rect = startup_from[3];
from_rect.offset((which_button > 0) ? 40 : 0,0);
to_rect = startup_button[which_position];
to_rect.left += 4; to_rect.top += 4;
to_rect.right = to_rect.left + 40;
to_rect.bottom = to_rect.top + 40;
rect_draw_some_item(*ResMgr::graphics.get("startup",true),from_rect,mainPtr,to_rect);
rect_draw_some_item(startup_gworld,from_rect,mainPtr,to_rect);
TextStyle style;
style.font = FONT_DUNGEON;
@@ -477,22 +481,36 @@ void end_startup() {
item_sbar->show();
}
static void loadImageToRenderTexture(sf::RenderTexture& tex, std::string imgName) {
sf::Texture& temp_gworld = *ResMgr::graphics.get(imgName);
rectangle texrect(temp_gworld);
/* FIXME: actually the resolution of imgName sets the resolution of the final texture zone.
It may be better to add another Vector2u to define this resolution independly of imgName */
static rectangle loadImageToRenderTexture(sf::RenderTexture& tex, std::string imgName) {
auto const& temp_gworld = *ResMgr::textures.get(imgName);
rectangle texrect(*temp_gworld);
tex.create(texrect.width(), texrect.height());
rect_draw_some_item(temp_gworld, texrect, tex, texrect, sf::BlendNone);
rect_draw_some_item(temp_gworld, rectangle(temp_gworld), tex, texrect, sf::BlendNone);
// now update the viewport so that a picture draw in 0,0,dim.y,dim.x fills the texture
sf::View view;
view.reset(sf::FloatRect(0, 0, texrect.width(), texrect.height()));
sf::FloatRect viewport;
viewport.left = 0;
viewport.top = 0;
viewport.width = float(texrect.width())/temp_gworld.dimension.x;
viewport.height = float(texrect.height())/temp_gworld.dimension.y;
view.setViewport(viewport);
tex.setView(view);
return rectangle(0,0,temp_gworld.dimension.y,temp_gworld.dimension.x);
}
void load_main_screen() {
// Preload the main game interface images
ResMgr::graphics.get("invenbtns");
loadImageToRenderTexture(terrain_screen_gworld, "terscreen");
ResMgr::textures.get("invenbtns");
terrain_screen_rect=loadImageToRenderTexture(terrain_screen_gworld, "terscreen");
loadImageToRenderTexture(pc_stats_gworld, "statarea");
loadImageToRenderTexture(item_stats_gworld, "inventory");
loadImageToRenderTexture(text_area_gworld, "transcript");
text_area_rect=loadImageToRenderTexture(text_area_gworld, "transcript");
loadImageToRenderTexture(text_bar_gworld, "textbar");
ResMgr::graphics.get("buttons");
ResMgr::textures.get("buttons");
}
void redraw_screen(int refresh) {
@@ -622,7 +640,7 @@ void draw_text_bar() {
void put_text_bar(std::string str) {
text_bar_gworld.setActive(false);
auto& bar_gw = *ResMgr::graphics.get("textbar");
auto const &bar_gw = *ResMgr::textures.get("textbar");
rect_draw_some_item(bar_gw, rectangle(bar_gw), text_bar_gworld, rectangle(bar_gw));
TextStyle style;
style.colour = sf::Color::White;

View File

@@ -121,7 +121,7 @@ void show_logo() {
if (ui_scale<1) ui_scale=1;
rectangle logo_from = {0,0,int(ui_scale*350),int(ui_scale*350)};
logo_from.offset((whole_window.right - logo_from.right) / 2,(whole_window.bottom - logo_from.bottom) / 2);
sf::Texture& pict_to_draw = *ResMgr::graphics.get("spidlogo", true);
auto const &pict_to_draw = *ResMgr::textures.get("spidlogo", true);
play_sound(-95);
while(sound_going(95)) {
@@ -144,7 +144,7 @@ void plop_fancy_startup() {
whole_window = rectangle(mainPtr);
sf::Time delay = time_in_ticks(220);
intro_from.offset((whole_window.right - intro_from.right) / 2,(whole_window.bottom - intro_from.bottom) / 2);
sf::Texture& pict_to_draw = *ResMgr::graphics.get("startsplash", true);
auto const & pict_to_draw = *ResMgr::textures.get("startsplash", true);
play_sound(-22);
sf::Clock timer;

View File

@@ -65,6 +65,7 @@ extern location center;
extern cCustomGraphics spec_scen_g;
extern sf::RenderTexture pc_stats_gworld, item_stats_gworld, text_area_gworld;
extern sf::RenderTexture terrain_screen_gworld;
extern rectangle text_area_rect;
// game globals
extern enum_map(eItemButton, rectangle) item_buttons[8];
@@ -96,8 +97,9 @@ void put_pc_screen() {
pc_stats_gworld.setActive();
// First clean up gworld with pretty patterns
sf::Texture& orig = *ResMgr::graphics.get("statarea");
rect_draw_some_item(orig, rectangle(orig), pc_stats_gworld, rectangle(pc_stats_gworld));
auto const &orig = *ResMgr::textures.get("statarea");
rectangle const stats_rect(orig);
rect_draw_some_item(orig, stats_rect, pc_stats_gworld, stats_rect);
tileImage(pc_stats_gworld, erase_rect,bg[6]);
TextStyle style;
@@ -217,8 +219,9 @@ void put_item_screen(eItemWinMode screen_num) {
item_stats_gworld.setActive(false);
// First clean up gworld with pretty patterns
sf::Texture& orig = *ResMgr::graphics.get("inventory");
rect_draw_some_item(orig, rectangle(orig), item_stats_gworld, rectangle(item_stats_gworld));
auto const & orig = *ResMgr::textures.get("inventory");
rectangle const item_stats_rect(orig);
rect_draw_some_item(orig, item_stats_rect, item_stats_gworld, item_stats_rect);
tileImage(item_stats_gworld, erase_rect,bg[6]);
// Draw buttons at bottom
@@ -943,7 +946,7 @@ void add_string_to_buf(std::string str, unsigned short indent) {
inited = true;
buf_style.font = FONT_PLAIN;
buf_style.pointSize = 12;
width = text_area_gworld.getSize().x - 5;
width = text_area_rect.width() - 5;
}
if(overall_mode == MODE_STARTUP)
return;

View File

@@ -155,7 +155,7 @@ void cToolbar::draw_buttons() {
cache.create(266,38);
cache.clear(sf::Color::Black);
}
sf::Texture& buttons_gworld = *ResMgr::graphics.get("buttons");
auto const & buttons_gworld = *ResMgr::textures.get("buttons");
for(const auto& btn : toolbar) {
rectangle icon_rect = {0, 0, 32, 32}, to_rect = btn.bounds;
location slot = btn_pos(btn.btn);

View File

@@ -131,7 +131,7 @@ void cCustomGraphics::convert_sheets() {
fs::path sheetPath = pic_dir/("sheet" + std::to_string(i) + ".png");
sheets[i]->copyToImage().saveToFile(sheetPath.string().c_str());
}
ResMgr::graphics.pushPath(pic_dir);
ResMgr::textures.pushPath(pic_dir);
}
void cCustomGraphics::replace_sheet(size_t num, sf::Image& newSheet) {

View File

@@ -14,6 +14,8 @@
#include "fileio.hpp"
#include "render_shapes.hpp"
#include "texture.hpp"
#include "res_image.hpp"
sf::Shader maskShader;
extern fs::path progDir;
@@ -60,6 +62,56 @@ void init_shaders() {
} while(false);
}
void draw_splash(const Texture& splash, sf::RenderWindow& targ, rectangle dest_rect) {
targ.clear(sf::Color::Black);
rect_draw_some_item(splash, splash.dimension, targ, dest_rect);
targ.display();
}
static void rect_draw_some_item(const sf::Texture& src_gworld,rectangle src_rect,sf::RenderTarget& targ_gworld,rectangle targ_rect,sf::RenderStates mode);
static void rect_draw_some_item(const Texture& src_gworld,rectangle src_rect,sf::RenderTarget& targ_gworld,rectangle targ_rect,sf::RenderStates mode);
void rect_draw_some_item(const Texture& src_gworld,rectangle src_rect,sf::RenderTarget& targ_gworld,rectangle targ_rect,sf::BlendMode mode){
rect_draw_some_item(src_gworld, src_rect, targ_gworld, targ_rect, sf::RenderStates(mode));
}
static rectangle rescale(rectangle const &orig, sf::Vector2u const &fromSize, sf::Vector2u const &toSize)
{
float const scale[]={float(toSize.x)/fromSize.x, float(toSize.y)/fromSize.y};
return rectangle(int(scale[1]*orig.top), int(scale[0]*orig.left), int(scale[1]*orig.bottom), int(scale[0]*orig.right));
}
void rect_draw_some_item(const Texture& src_gworld,rectangle src_rect,sf::RenderTarget& targ_gworld,rectangle targ_rect,sf::RenderStates mode) {
setActiveRenderTarget(targ_gworld);
src_rect=rescale(src_rect, src_gworld.dimension, src_gworld->getSize());
sf::Sprite tile(*src_gworld, src_rect);
tile.setPosition(targ_rect.left, targ_rect.top);
double xScale = targ_rect.width(), yScale = targ_rect.height();
xScale /= src_rect.width();
yScale /= src_rect.height();
tile.setScale(xScale, yScale);
targ_gworld.draw(tile, mode);
}
void rect_draw_some_item(const Texture& src_gworld,rectangle src_rect,const sf::Texture& mask_gworld,sf::RenderTarget& targ_gworld,rectangle targ_rect) {
rectangle real_src_rect=rescale(src_rect, src_gworld.dimension, src_gworld->getSize());
static sf::RenderTexture src;
static bool inited = false;
if(!inited || real_src_rect.width() != src.getSize().x || real_src_rect.height() != src.getSize().y) {
src.create(real_src_rect.width(), real_src_rect.height());
inited = true;
}
rectangle dest_rect = real_src_rect;
dest_rect.offset(-dest_rect.left,-dest_rect.top);
rect_draw_some_item(src_gworld, real_src_rect, src, dest_rect);
src.display();
maskShader.setParameter("texture", sf::Shader::CurrentTexture);
maskShader.setParameter("mask", mask_gworld);
rect_draw_some_item(src.getTexture(), dest_rect, targ_gworld, targ_rect, &maskShader);
}
void draw_splash(const sf::Texture& splash, sf::RenderWindow& targ, rectangle dest_rect) {
rectangle from_rect = rectangle(splash);
targ.clear(sf::Color::Black);
@@ -67,8 +119,6 @@ void draw_splash(const sf::Texture& splash, sf::RenderWindow& targ, rectangle de
targ.display();
}
static void rect_draw_some_item(const sf::Texture& src_gworld,rectangle src_rect,sf::RenderTarget& targ_gworld,rectangle targ_rect,sf::RenderStates mode);
void rect_draw_some_item(sf::RenderTarget& targ_gworld,rectangle targ_rect) {
fill_rect(targ_gworld, targ_rect, sf::Color::Black);
}

View File

@@ -24,6 +24,11 @@ void rect_draw_some_item(const sf::Texture& src_gworld,rectangle src_rect,sf::Re
void rect_draw_some_item(const sf::Texture& src_gworld,rectangle src_rect,const sf::Texture& mask_gworld,sf::RenderTarget& targ_gworld,rectangle targ_rect);
void draw_splash(const sf::Texture& splash, sf::RenderWindow& targ, rectangle dest_rect);
struct Texture;
void rect_draw_some_item(const Texture & src_gworld,rectangle src_rect,sf::RenderTarget& targ_gworld,rectangle targ_rect,sf::BlendMode mode = sf::BlendNone);
void rect_draw_some_item(const Texture& src_gworld,rectangle src_rect,const sf::Texture& mask_gworld,sf::RenderTarget& targ_gworld,rectangle targ_rect);
void draw_splash(const Texture & splash, sf::RenderWindow& targ, rectangle dest_rect);
void setActiveRenderTarget(sf::RenderTarget& where);
#endif

59
src/gfx/texture.hpp Normal file
View File

@@ -0,0 +1,59 @@
//
// texture.hpp
// Common Data Files
//
// Created by alonso on 15/10/2020.
//
#ifndef BoE_TEXTURE_HPP
#define BoE_TEXTURE_HPP
#include <memory>
#include <string>
#include <SFML/Graphics.hpp>
#include "location.hpp"
struct Texture {
operator bool() const {
return bool(texture);
}
sf::Texture const &operator*() const {
return *texture;
}
sf::Texture const *operator->() const {
return texture.get();
}
sf::Texture *operator->() {
return texture.get();
}
operator rectangle() const {
return rectangle(0, 0, dimension.y, dimension.x);
}
static sf::Vector2u getApplicationDimension(std::string const &name) {
static std::map<std::string, sf::Vector2u> nameToDimensions = {
{ "buttons.png", {192,115} },
{ "spidlogo.png", {350,350} },
{ "inventory.png", {271,144} },
{ "pcedbuttons", {114,57} },
{ "startanim.png", {280,590} },
{ "startbut.png", {301,48} },
{ "startsplash.png", {640,480} },
{ "startup.png", {602, 322} },
{ "statarea.png", {271,116} },
{ "terscreen.png", {278,350} },
{ "textbar.png", {279,22} },
{ "transcript.png", {256,138} },
};
auto const &it=nameToDimensions.find(name);
if (it!=nameToDimensions.end())
return it->second;
else
return {0,0};
}
std::shared_ptr<sf::Texture> texture;
sf::Vector2u dimension;
};
#endif /* texture_h */

View File

@@ -193,7 +193,7 @@ void Set_up_win () {
ResMgr::graphics.get("invenbtns");
ResMgr::graphics.get("staticons");
ResMgr::graphics.get("dlogpics");
ResMgr::graphics.get("pcedbuttons");
ResMgr::textures.get("pcedbuttons");
ResMgr::graphics.get("pcs");
}
@@ -384,7 +384,7 @@ void display_party() {
win_draw_string(mainPtr,no_party_rect,"No party loaded.",eTextMode::WRAP,style);
}
else {
sf::Texture& buttons_gworld = *ResMgr::graphics.get("pcedbuttons");
auto const &buttons_gworld = *ResMgr::textures.get("pcedbuttons");
from_rect = pc_info_rect;
from_rect.top = from_rect.bottom - 11;
if(!party_in_scen)
@@ -397,7 +397,6 @@ void display_party() {
else fill_rect(mainPtr, pc_area_buttons[i][0], sf::Color::Black);
from_rect = (current_pressed_button == i) ? ed_buttons_from[1] : ed_buttons_from[0];
rect_draw_some_item(buttons_gworld,from_rect,mainPtr,pc_area_buttons[i][0], sf::BlendAdd);
if(univ.party[i].main_status != eMainStatus::ABSENT) { // PC exists?

View File

@@ -3355,7 +3355,7 @@ void edit_custom_sheets() {
spec_scen_g.init_sheet(0);
spec_scen_g.sheets[0]->copyToImage().saveToFile((pic_dir/"sheet0.png").string().c_str());
all_pics.insert(all_pics.begin(), 0);
ResMgr::graphics.pushPath(pic_dir);
ResMgr::textures.pushPath(pic_dir);
}
set_cursor(watch_curs);