307 lines
9.2 KiB
C++
307 lines
9.2 KiB
C++
#include "screen.h"
|
|
#include <SDL3/SDL_pixels.h> // Para SDL_PIXELFORMAT_RGBA8888
|
|
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
|
|
#include <algorithm> // Para max, min
|
|
#include <fstream> // Para basic_ifstream, ifstream
|
|
#include <iterator> // Para istreambuf_iterator, operator==
|
|
#include <string> // Para allocator, char_traits, operator+, to_s...
|
|
#include "asset.h" // Para Asset
|
|
#include "dbgtxt.h" // Para dbg_print
|
|
#include "global_inputs.h" // Para service_pressed_counter
|
|
#include "jail_shader.h" // Para init, render
|
|
#include "mouse.h" // Para updateCursorVisibility
|
|
#include "notifier.h" // Para Notifier
|
|
#include "on_screen_help.h" // Para OnScreenHelp
|
|
#include "options.h" // Para Options, OptionsVideo, options, Options...
|
|
#include "resource.h" // Para Resource
|
|
|
|
// [SINGLETON]
|
|
Screen *Screen::screen_ = nullptr;
|
|
|
|
// [SINGLETON] Crearemos el objeto con esta función estática
|
|
void Screen::init(SDL_Window *window, SDL_Renderer *renderer) { Screen::screen_ = new Screen(window, renderer); }
|
|
|
|
// [SINGLETON] Destruiremos el objeto con esta función estática
|
|
void Screen::destroy() { delete Screen::screen_; }
|
|
|
|
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
|
|
Screen *Screen::get() { return Screen::screen_; }
|
|
|
|
// Constructor
|
|
Screen::Screen(SDL_Window *window, SDL_Renderer *renderer)
|
|
: window_(window),
|
|
renderer_(renderer),
|
|
game_canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
|
|
src_rect_(SDL_FRect{0, 0, static_cast<float>(param.game.width), static_cast<float>(param.game.height)}),
|
|
dst_rect_(SDL_FRect{0, 0, static_cast<float>(param.game.width), static_cast<float>(param.game.height)})
|
|
{
|
|
// Inicializa variables
|
|
auto DM = SDL_GetCurrentDisplayMode(0);
|
|
info_resolution_ = std::to_string(DM->w) + " X " + std::to_string(DM->h) + " AT " + std::to_string(DM->refresh_rate) + " HZ";
|
|
adjustRenderLogicalSize();
|
|
|
|
// Muestra la ventana
|
|
show();
|
|
|
|
// Inicializa los shaders
|
|
initShaders(); // Se ha de ejecutar con la ventana visible
|
|
}
|
|
|
|
// Destructor
|
|
Screen::~Screen() { SDL_DestroyTexture(game_canvas_); }
|
|
|
|
// Limpia la pantalla
|
|
void Screen::clean(Color color)
|
|
{
|
|
SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 0xFF);
|
|
SDL_RenderClear(renderer_);
|
|
}
|
|
|
|
// Prepara para empezar a dibujar en la textura de juego
|
|
void Screen::start() { SDL_SetRenderTarget(renderer_, game_canvas_); }
|
|
|
|
// Vuelca el contenido del renderizador en pantalla
|
|
void Screen::render()
|
|
{
|
|
fps_.increment();
|
|
|
|
// Renderiza todos los overlays y efectos
|
|
renderOverlays();
|
|
|
|
// Renderiza el contenido del game_canvas_
|
|
renderScreen();
|
|
}
|
|
|
|
// Renderiza el contenido del game_canvas_
|
|
void Screen::renderScreen()
|
|
{
|
|
SDL_SetRenderTarget(renderer_, nullptr);
|
|
clean();
|
|
|
|
if (options.video.shaders)
|
|
{
|
|
shader::render();
|
|
}
|
|
else
|
|
{
|
|
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
|
|
SDL_RenderPresent(renderer_);
|
|
}
|
|
}
|
|
|
|
// Establece el modo de video
|
|
void Screen::setVideoMode(ScreenVideoMode mode)
|
|
{
|
|
// Actualiza las opciones
|
|
options.video.mode = mode;
|
|
|
|
// Configura el modo de pantalla
|
|
Uint32 flags = SDL_GetWindowFlags(window_);
|
|
if (flags != static_cast<Uint32>(options.video.mode))
|
|
{
|
|
SDL_SetWindowFullscreen(window_, static_cast<bool>(options.video.mode));
|
|
}
|
|
|
|
initShaders();
|
|
}
|
|
|
|
// Camibia entre pantalla completa y ventana
|
|
void Screen::toggleVideoMode()
|
|
{
|
|
options.video.mode = options.video.mode == ScreenVideoMode::WINDOW ? ScreenVideoMode::FULLSCREEN : ScreenVideoMode::WINDOW;
|
|
setVideoMode();
|
|
}
|
|
|
|
// Cambia el tamaño de la ventana
|
|
void Screen::setWindowZoom(int zoom)
|
|
{
|
|
options.video.window.zoom = zoom;
|
|
adjustWindowSize();
|
|
}
|
|
|
|
// Reduce el tamaño de la ventana
|
|
bool Screen::decWindowZoom()
|
|
{
|
|
if (options.video.mode == ScreenVideoMode::WINDOW)
|
|
{
|
|
const int PREVIOUS_ZOOM = options.video.window.zoom;
|
|
--options.video.window.zoom;
|
|
options.video.window.zoom = std::max(options.video.window.zoom, 1);
|
|
|
|
if (options.video.window.zoom != PREVIOUS_ZOOM)
|
|
{
|
|
adjustWindowSize();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Aumenta el tamaño de la ventana
|
|
bool Screen::incWindowZoom()
|
|
{
|
|
if (options.video.mode == ScreenVideoMode::WINDOW)
|
|
{
|
|
const int PREVIOUS_ZOOM = options.video.window.zoom;
|
|
++options.video.window.zoom;
|
|
options.video.window.zoom = std::min(options.video.window.zoom, options.video.window.max_zoom);
|
|
|
|
if (options.video.window.zoom != PREVIOUS_ZOOM)
|
|
{
|
|
adjustWindowSize();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Actualiza la lógica de la clase
|
|
void Screen::update()
|
|
{
|
|
fps_.calculate(SDL_GetTicks());
|
|
shake_effect_.update(src_rect_, dst_rect_);
|
|
flash_effect_.update();
|
|
Notifier::get()->update();
|
|
OnScreenHelp::get()->update();
|
|
Mouse::updateCursorVisibility();
|
|
}
|
|
|
|
// Actualiza y dibuja el efecto de flash en la pantalla
|
|
void Screen::renderFlash()
|
|
{
|
|
if (flash_effect_.isRendarable())
|
|
{
|
|
SDL_SetRenderDrawColor(renderer_, flash_effect_.color.r, flash_effect_.color.g, flash_effect_.color.b, 0xFF);
|
|
SDL_RenderClear(renderer_);
|
|
}
|
|
}
|
|
|
|
// Aplica el efecto de agitar la pantalla
|
|
void Screen::renderShake()
|
|
{
|
|
if (shake_effect_.enabled)
|
|
{
|
|
// Guarda el renderizador actual para dejarlo despues como estaba
|
|
auto current_target = SDL_GetRenderTarget(renderer_);
|
|
|
|
// Crea una textura temporal
|
|
auto temp_texture = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
|
|
|
|
// Vuelca game_canvas_ a la textura temporal
|
|
SDL_SetRenderTarget(renderer_, temp_texture);
|
|
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
|
|
|
|
// Vuelca textura temporal a game_canvas_
|
|
SDL_SetRenderTarget(renderer_, game_canvas_);
|
|
SDL_RenderTexture(renderer_, temp_texture, &src_rect_, &dst_rect_);
|
|
|
|
// Elimina la textura temporal
|
|
SDL_DestroyTexture(temp_texture);
|
|
|
|
// Restaura el renderizador de destino original
|
|
SDL_SetRenderTarget(renderer_, current_target);
|
|
}
|
|
}
|
|
|
|
// Muestra información por pantalla
|
|
void Screen::renderInfo()
|
|
{
|
|
if (show_debug_info_ && Resource::get())
|
|
{
|
|
auto text = Resource::get()->getText("smb2");
|
|
|
|
// FPS
|
|
const std::string FPS_TEXT = std::to_string(fps_.lastValue) + " FPS";
|
|
text->writeColored(param.game.width - text->lenght(FPS_TEXT), 0, FPS_TEXT, orange_soft_color);
|
|
|
|
// Resolution
|
|
text->writeColored(0, 0, info_resolution_, orange_soft_color);
|
|
}
|
|
}
|
|
|
|
// Carga el contenido del archivo GLSL
|
|
void Screen::loadShaders()
|
|
{
|
|
const std::string GLSL_FILE = param.game.game_area.rect.h == 256 ? "crtpi_256.glsl" : "crtpi_240.glsl";
|
|
std::ifstream f(Asset::get()->get(GLSL_FILE).c_str());
|
|
shader_source_ = std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
|
}
|
|
|
|
// Inicializa los shaders
|
|
void Screen::initShaders()
|
|
{
|
|
if (shader_source_.empty())
|
|
{
|
|
loadShaders();
|
|
}
|
|
shader::init(window_, game_canvas_, shader_source_);
|
|
}
|
|
|
|
// Calcula el tamaño de la ventana
|
|
void Screen::adjustWindowSize()
|
|
{
|
|
options.video.window.max_zoom = getMaxZoom();
|
|
|
|
// Establece el nuevo tamaño
|
|
if (options.video.mode == ScreenVideoMode::WINDOW)
|
|
{
|
|
const int WIDTH = param.game.width * options.video.window.zoom;
|
|
const int HEIGHT = param.game.height * options.video.window.zoom;
|
|
|
|
int old_width, old_height;
|
|
SDL_GetWindowSize(window_, &old_width, &old_height);
|
|
|
|
int old_pos_x, old_pos_y;
|
|
SDL_GetWindowPosition(window_, &old_pos_x, &old_pos_y);
|
|
|
|
const int NEW_POS_X = old_pos_x + (old_width - WIDTH) / 2;
|
|
const int NEW_POS_Y = old_pos_y + (old_height - HEIGHT) / 2;
|
|
|
|
SDL_FRect viewport = {0, 0, WIDTH, HEIGHT};
|
|
SDL_SetRenderViewport(renderer_, &viewport);
|
|
|
|
SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS_), std::max(NEW_POS_Y, 0));
|
|
SDL_SetWindowSize(window_, WIDTH, HEIGHT);
|
|
|
|
initShaders();
|
|
}
|
|
}
|
|
|
|
// Obtiene el tamaño máximo de zoom posible para la ventana
|
|
int Screen::getMaxZoom()
|
|
{
|
|
// Obtiene información sobre la pantalla
|
|
auto DM = SDL_GetCurrentDisplayMode(0);
|
|
|
|
// Calcula el máximo factor de zoom que se puede aplicar a la pantalla
|
|
const int MAX_ZOOM = std::min(DM->w / param.game.width, (DM->h - WINDOWS_DECORATIONS_) / param.game.height);
|
|
|
|
// Normaliza los valores de zoom
|
|
options.video.window.zoom = std::min(options.video.window.zoom, MAX_ZOOM);
|
|
|
|
return MAX_ZOOM;
|
|
}
|
|
|
|
// Renderiza todos los overlays y efectos
|
|
void Screen::renderOverlays()
|
|
{
|
|
// Dibuja efectos y elementos sobre el game_canvas_
|
|
renderShake();
|
|
renderFlash();
|
|
renderAttenuate();
|
|
OnScreenHelp::get()->render();
|
|
renderInfo();
|
|
Notifier::get()->render();
|
|
}
|
|
|
|
// Atenua la pantalla
|
|
void Screen::renderAttenuate()
|
|
{
|
|
if (attenuate_effect_)
|
|
{
|
|
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 64);
|
|
SDL_RenderFillRect(renderer_, nullptr);
|
|
}
|
|
} |