395 lines
12 KiB
C++
395 lines
12 KiB
C++
#include "screen.h"
|
|
#include <SDL2/SDL_pixels.h> // Para SDL_PIXELFORMAT_RGBA8888
|
|
#include <SDL2/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, operator+, char_traits, to_s...
|
|
#include <vector> // Para vector
|
|
#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...
|
|
|
|
// [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_({0, 0, param.game.width, param.game.height}),
|
|
dst_rect_({0, 0, param.game.width, param.game.height})
|
|
{
|
|
// Inicializa variables
|
|
SDL_DisplayMode DM;
|
|
SDL_GetCurrentDisplayMode(0, &DM);
|
|
info_resolution_ = std::to_string(DM.w) + " X " + std::to_string(DM.h) + " AT " + std::to_string(DM.refresh_rate) + " HZ";
|
|
adjustRenderLogicalSize();
|
|
|
|
// Inicializa los shaders
|
|
initShaders();
|
|
|
|
// Muestra la ventana
|
|
show();
|
|
}
|
|
|
|
// 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()
|
|
{
|
|
// 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_RenderCopy(renderer_, game_canvas_, nullptr, nullptr);
|
|
SDL_RenderPresent(renderer_);
|
|
}
|
|
}
|
|
|
|
// Establece el modo de video
|
|
void Screen::setVideoMode(ScreenVideoMode video_mode)
|
|
{
|
|
// Actualiza las opciones
|
|
options.video.mode = video_mode;
|
|
|
|
// Configura el modo de pantalla
|
|
Uint32 flags = SDL_GetWindowFlags(window_);
|
|
if (flags != static_cast<Uint32>(options.video.mode))
|
|
{
|
|
SDL_SetWindowFullscreen(window_, static_cast<Uint32>(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;
|
|
}
|
|
|
|
// Cambia el tipo de mezcla
|
|
void Screen::setBlendMode(SDL_BlendMode blendMode) { SDL_SetRenderDrawBlendMode(renderer_, blendMode); }
|
|
|
|
// Actualiza la lógica de la clase
|
|
void Screen::update()
|
|
{
|
|
updateShakeEffect();
|
|
updateFlash();
|
|
Notifier::get()->update();
|
|
updateFPS();
|
|
OnScreenHelp::get()->update();
|
|
Mouse::updateCursorVisibility();
|
|
}
|
|
|
|
// Agita la pantalla
|
|
void Screen::shake()
|
|
{
|
|
// Si no hay un shake effect activo, se guarda una copia de los valores actuales antes de modificarlos
|
|
if (!shake_effect_.enabled)
|
|
{
|
|
shake_effect_.enabled = true;
|
|
shake_effect_.originalPos = src_rect_.x;
|
|
shake_effect_.originalWidth = src_rect_.w;
|
|
src_rect_.w -= shake_effect_.desp;
|
|
dst_rect_.w = src_rect_.w;
|
|
}
|
|
|
|
// Si ya hay un shake effect en marcha no se pilla el origen, solo se renuevan los contadores
|
|
shake_effect_.remaining = shake_effect_.lenght;
|
|
shake_effect_.counter = shake_effect_.delay;
|
|
}
|
|
|
|
// Actualiza la logica para agitar la pantalla
|
|
void Screen::updateShakeEffect()
|
|
{
|
|
if (shake_effect_.enabled)
|
|
{
|
|
if (shake_effect_.counter > 0)
|
|
{
|
|
shake_effect_.counter--;
|
|
}
|
|
else
|
|
{
|
|
shake_effect_.counter = shake_effect_.delay;
|
|
const auto srcdesp = shake_effect_.remaining % 2 == 0 ? 0 : shake_effect_.desp;
|
|
const auto dstdesp = shake_effect_.remaining % 2 == 1 ? 0 : shake_effect_.desp;
|
|
src_rect_.x = shake_effect_.originalPos + srcdesp;
|
|
dst_rect_.x = shake_effect_.originalPos + dstdesp;
|
|
shake_effect_.remaining--;
|
|
shake_effect_.enabled = shake_effect_.remaining == -1 ? false : true;
|
|
if (!shake_effect_.enabled)
|
|
{
|
|
src_rect_.x = shake_effect_.originalPos;
|
|
src_rect_.w = shake_effect_.originalWidth;
|
|
dst_rect_ = src_rect_;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pone la pantalla de color
|
|
void Screen::flash(Color color, int lenght, int delay) { flash_effect_ = FlashEffect(true, lenght, delay, color); }
|
|
|
|
// 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_);
|
|
}
|
|
}
|
|
|
|
// Actualiza el efecto de flash
|
|
void Screen::updateFlash() { flash_effect_.update(); }
|
|
|
|
// Atenua la pantalla
|
|
void Screen::renderAttenuate()
|
|
{
|
|
if (attenuate_effect_)
|
|
{
|
|
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 64);
|
|
SDL_RenderFillRect(renderer_, nullptr);
|
|
}
|
|
}
|
|
|
|
// 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_RenderCopy(renderer_, game_canvas_, nullptr, nullptr);
|
|
|
|
// Vuelca textura temporal a game_canvas_
|
|
SDL_SetRenderTarget(renderer_, game_canvas_);
|
|
SDL_RenderCopy(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);
|
|
}
|
|
}
|
|
|
|
// Activa / desactiva los shaders
|
|
void Screen::toggleShaders()
|
|
{
|
|
options.video.shaders = !options.video.shaders;
|
|
}
|
|
|
|
// Activa / desactiva la información de debug
|
|
void Screen::toggleDebugInfo() { show_debug_info_ = !show_debug_info_; }
|
|
|
|
// Atenua la pantalla
|
|
void Screen::attenuate(bool value) { attenuate_effect_ = value; }
|
|
|
|
// Calcula los frames por segundo
|
|
void Screen::updateFPS()
|
|
{
|
|
if (SDL_GetTicks() - fps_ticks_ > 1000)
|
|
{
|
|
fps_ticks_ = SDL_GetTicks();
|
|
fps_ = fps_counter_;
|
|
fps_counter_ = 0;
|
|
}
|
|
}
|
|
|
|
// Muestra información por pantalla
|
|
void Screen::renderInfo()
|
|
{
|
|
if (show_debug_info_)
|
|
{
|
|
// FPS
|
|
const std::string fpstext = std::to_string(fps_) + " FPS";
|
|
dbg_print(param.game.width - fpstext.length() * 8, 0, fpstext.c_str(), 255, 255, 0);
|
|
|
|
// Resolution
|
|
dbg_print(0, 0, info_resolution_.c_str(), 255, 255, 0);
|
|
|
|
// Contador de service_pressed_counter
|
|
if (const int counter = globalInputs::service_pressed_counter; counter > 0)
|
|
{
|
|
dbg_print(0, 8, std::to_string(counter).c_str(), 255, 0, 255);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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());
|
|
shaderSource = std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
|
}
|
|
|
|
// Inicializa los shaders
|
|
void Screen::initShaders()
|
|
{
|
|
if (shaderSource.empty())
|
|
{
|
|
loadShaders();
|
|
}
|
|
shader::init(window_, game_canvas_, shaderSource.c_str());
|
|
}
|
|
|
|
// 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_Rect viewport = {0, 0, WIDTH, HEIGHT};
|
|
SDL_RenderSetViewport(renderer_, &viewport);
|
|
|
|
SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS_), std::max(NEW_POS_Y, 0));
|
|
SDL_SetWindowSize(window_, WIDTH, HEIGHT);
|
|
|
|
initShaders();
|
|
}
|
|
}
|
|
|
|
// Ajusta el tamaño lógico del renderizador
|
|
void Screen::adjustRenderLogicalSize() { SDL_RenderSetLogicalSize(renderer_, param.game.width, param.game.height); }
|
|
|
|
// Obtiene el tamaño máximo de zoom posible para la ventana
|
|
int Screen::getMaxZoom()
|
|
{
|
|
// Obtiene información sobre la pantalla
|
|
SDL_DisplayMode DM;
|
|
SDL_GetCurrentDisplayMode(0, &DM);
|
|
|
|
// 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()
|
|
{
|
|
// Actualiza el contador de FPS
|
|
++fps_counter_;
|
|
|
|
// Dibuja efectos y elementos sobre el game_canvas_
|
|
renderShake();
|
|
renderFlash();
|
|
renderAttenuate();
|
|
OnScreenHelp::get()->render();
|
|
renderInfo();
|
|
Notifier::get()->render();
|
|
} |