422 lines
12 KiB
C++
422 lines
12 KiB
C++
#include "screen.h"
|
|
#include <SDL2/SDL_events.h> // Para SDL_DISABLE, SDL_ENABLE
|
|
#include <SDL2/SDL_mouse.h> // Para SDL_ShowCursor
|
|
#include <SDL2/SDL_pixels.h> // Para SDL_PIXELFORMAT_RGBA8888
|
|
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks
|
|
#include <algorithm> // Para clamp, 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 "notifier.h" // Para Notifier
|
|
#include "on_screen_help.h" // Para OnScreenHelp
|
|
#include "options.h" // Para Options, OptionsVideo, options, Options...
|
|
#include "mouse.h"
|
|
|
|
#ifndef NO_SHADERS
|
|
#include "jail_shader.h" // para init, render
|
|
#endif
|
|
|
|
// [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado
|
|
Screen *Screen::screen_ = nullptr;
|
|
|
|
// [SINGLETON] Crearemos el objeto screen 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 screen con esta función estática
|
|
void Screen::destroy()
|
|
{
|
|
delete Screen::screen_;
|
|
}
|
|
|
|
// [SINGLETON] Con este método obtenemos el objeto screen 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";
|
|
|
|
// Establece el modo de video
|
|
setVideoMode(options.video.mode);
|
|
|
|
// Muestra la ventana
|
|
SDL_ShowWindow(window_);
|
|
}
|
|
|
|
// 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()
|
|
{
|
|
// Actualiza el contador de FPS
|
|
++fps_counter_;
|
|
|
|
// Dibuja efectos y elementos sobre el game_canvas_
|
|
renderFlash();
|
|
renderAttenuate();
|
|
OnScreenHelp::get()->render();
|
|
renderInfo();
|
|
Notifier::get()->render();
|
|
|
|
// Restablece el objetivo de renderizado al buffer de pantalla predeterminado
|
|
SDL_SetRenderTarget(renderer_, nullptr);
|
|
|
|
// Renderiza el contenido del game_canvas_ o aplica shaders según configuración
|
|
renderScreen();
|
|
}
|
|
|
|
// Selecciona y ejecuta el método de renderizado adecuado basado en la configuración de shaders
|
|
void Screen::renderScreen()
|
|
{
|
|
#ifdef NO_SHADERS
|
|
// Actualiza la pantalla con el contenido del game_canvas_
|
|
presentGameCanvas();
|
|
#else
|
|
if (options.video.shaders)
|
|
{
|
|
// Aplica shaders y renderiza el contenido
|
|
shader::render();
|
|
}
|
|
else
|
|
{
|
|
// Actualiza la pantalla con el contenido del game_canvas_
|
|
presentGameCanvas();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Establece el modo de video
|
|
void Screen::setVideoMode(ScreenVideoMode videoMode)
|
|
{
|
|
#ifdef ARCADE
|
|
options.video.mode = ScreenVideoMode::WINDOW;
|
|
#else
|
|
options.video.mode = videoMode;
|
|
#endif
|
|
|
|
switch (options.video.mode)
|
|
{
|
|
case ScreenVideoMode::WINDOW:
|
|
{
|
|
// Cambia a modo de ventana
|
|
SDL_SetWindowFullscreen(window_, 0);
|
|
|
|
#ifdef ARCADE
|
|
// Oculta el puntero
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
#else
|
|
// Muestra el puntero
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
#endif
|
|
// Modifica el tamaño de la ventana
|
|
SDL_Point pos = getNewPosition();
|
|
SDL_SetWindowSize(window_, param.game.width * options.video.window.size, param.game.height * options.video.window.size);
|
|
SDL_SetWindowPosition(window_, pos.x, pos.y);
|
|
|
|
break;
|
|
}
|
|
|
|
// Si está activo el modo de pantalla completa añade el borde
|
|
case ScreenVideoMode::FULLSCREEN:
|
|
{
|
|
// Aplica el modo de video
|
|
SDL_SetWindowFullscreen(window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
|
|
// Oculta el puntero
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Reinicia los shaders
|
|
if (options.video.shaders)
|
|
{
|
|
#ifndef NO_SHADERS
|
|
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());
|
|
std::string source((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
|
|
|
shader::init(window_, game_canvas_, source.c_str());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Camibia entre pantalla completa y ventana
|
|
void Screen::toggleVideoMode()
|
|
{
|
|
options.video.mode = options.video.mode == ScreenVideoMode::WINDOW ? ScreenVideoMode::FULLSCREEN : ScreenVideoMode::WINDOW;
|
|
setVideoMode(options.video.mode);
|
|
}
|
|
|
|
// Cambia el tamaño de la ventana
|
|
void Screen::setWindowSize(int size)
|
|
{
|
|
options.video.window.size = size;
|
|
setVideoMode(ScreenVideoMode::WINDOW);
|
|
}
|
|
|
|
// Reduce el tamaño de la ventana
|
|
void Screen::decWindowSize()
|
|
{
|
|
--options.video.window.size;
|
|
options.video.window.size = std::max(options.video.window.size, 1);
|
|
setVideoMode(ScreenVideoMode::WINDOW);
|
|
}
|
|
|
|
// Aumenta el tamaño de la ventana
|
|
void Screen::incWindowSize()
|
|
{
|
|
++options.video.window.size;
|
|
options.video.window.size = std::min(options.video.window.size, options.video.window.max_size);
|
|
setVideoMode(ScreenVideoMode::WINDOW);
|
|
}
|
|
|
|
// Cambia el color del borde
|
|
void Screen::setBorderColor(Color color)
|
|
{
|
|
border_color_ = color;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
// Activa / desactiva los shaders
|
|
void Screen::toggleShaders()
|
|
{
|
|
options.video.shaders = !options.video.shaders;
|
|
setVideoMode(options.video.mode);
|
|
const std::string value = options.video.shaders ? "on" : "off";
|
|
Notifier::get()->showText({"Shaders " + value});
|
|
}
|
|
|
|
// Activa / desactiva la información de debug
|
|
void Screen::toggleDebugInfo()
|
|
{
|
|
show_info_ = !show_info_;
|
|
}
|
|
|
|
// Atenua la pantalla
|
|
void Screen::attenuate(bool value)
|
|
{
|
|
attenuate_effect_ = value;
|
|
}
|
|
|
|
// Obtiene el puntero al renderizador
|
|
SDL_Renderer *Screen::getRenderer()
|
|
{
|
|
return renderer_;
|
|
}
|
|
|
|
// 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_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);
|
|
|
|
const std::string atten = attenuate_effect_ ? "ATTEN YES" : "ATTEN NO";
|
|
dbg_print(0, 16, atten.c_str(), 255, 0, 0);
|
|
}
|
|
}
|
|
|
|
// Calcula la nueva posición de la ventana a partir de la antigua al cambiarla de tamaño
|
|
SDL_Point Screen::getNewPosition()
|
|
{
|
|
// Obtiene la posición actual de la ventana
|
|
SDL_Point current_position;
|
|
SDL_GetWindowPosition(window_, ¤t_position.x, ¤t_position.y);
|
|
|
|
// Obtiene las dimensiones actuales de la ventana
|
|
int current_width, current_height;
|
|
SDL_GetWindowSize(window_, ¤t_width, ¤t_height);
|
|
|
|
// Obtiene las dimesiones que tendrá la ventana
|
|
const int new_width = param.game.width * options.video.window.size;
|
|
const int new_height = param.game.height * options.video.window.size;
|
|
|
|
// Obtiene el centro de la ventana actual
|
|
SDL_Point center;
|
|
center.x = current_position.x + current_width / 2;
|
|
center.y = current_position.y + current_height / 2;
|
|
|
|
// Calcula la nueva posición a partir del centro y las nuevas diemsiones
|
|
SDL_Point new_pos;
|
|
new_pos.x = center.x - new_width / 2;
|
|
new_pos.y = center.y - new_height / 2;
|
|
|
|
// Obtiene las dimensiones del escritorio
|
|
SDL_DisplayMode DM;
|
|
SDL_GetCurrentDisplayMode(0, &DM);
|
|
|
|
// Evita que la ventana quede fuera del escritorio
|
|
new_pos.x = std::clamp(new_pos.x, 30, DM.w - new_width);
|
|
new_pos.y = std::clamp(new_pos.y, 30, DM.h - new_height);
|
|
|
|
return new_pos;
|
|
}
|
|
|
|
// Actualiza la pantalla con el contenido del game_canvas_
|
|
void Screen::presentGameCanvas()
|
|
{
|
|
// Copia la textura de juego en el renderizador en la posición adecuada
|
|
if (shake_effect_.enabled)
|
|
{
|
|
// Esta copia es para evitar que se vea negro por los laterales
|
|
SDL_RenderCopy(renderer_, game_canvas_, nullptr, nullptr);
|
|
}
|
|
SDL_RenderCopy(renderer_, game_canvas_, &src_rect_, &dst_rect_);
|
|
|
|
// Actualiza la pantalla con el contenido del buffer de renderizado
|
|
SDL_RenderPresent(renderer_);
|
|
} |