492 lines
13 KiB
C++
492 lines
13 KiB
C++
#include "screen.h"
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <streambuf>
|
|
#ifndef NO_SHADERS
|
|
#include "jshader.h"
|
|
#endif
|
|
#include "dbgtxt.h"
|
|
#include "../service.h"
|
|
|
|
// Constructor
|
|
Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, Input *input, options_t *options)
|
|
{
|
|
// Copia punteros
|
|
this->window = window;
|
|
this->renderer = renderer;
|
|
this->asset = asset;
|
|
this->input = input;
|
|
this->options = options;
|
|
|
|
// Inicializa variables
|
|
SDL_GetRendererOutputSize(renderer, &windowWidth, &windowHeight);
|
|
gameCanvasWidth = options->video.gameWidth;
|
|
gameCanvasHeight = options->video.gameHeight;
|
|
dest = {0, 0, 0, 0};
|
|
borderColor = {0, 0, 0};
|
|
flashEffect.enabled = false;
|
|
flashEffect.counter = 0;
|
|
flashEffect.lenght = 0;
|
|
flashEffect.color = {0xFF, 0xFF, 0xFF};
|
|
shakeEffect.desp = 1;
|
|
shakeEffect.delay = 3;
|
|
shakeEffect.counter = 0;
|
|
shakeEffect.lenght = 8;
|
|
shakeEffect.remaining = 0;
|
|
shakeEffect.origin = 0;
|
|
attenuateEffect = false;
|
|
fpsTicks = 0;
|
|
fpsCounter = 0;
|
|
fps = 0;
|
|
#ifdef DEBUG
|
|
showInfo = true;
|
|
#else
|
|
showInfo = false;
|
|
#endif
|
|
SDL_DisplayMode DM;
|
|
SDL_GetCurrentDisplayMode(0, &DM);
|
|
infoResolution = std::to_string(DM.w) + " X " + std::to_string(DM.h) + " AT " + std::to_string(DM.refresh_rate) + " HZ";
|
|
|
|
// Crea los objetos
|
|
notify = new Notify(renderer, asset->get("notify.png"), asset->get("8bithud.png"), asset->get("8bithud.txt"), asset->get("notify.wav"), options);
|
|
|
|
// Define el color del borde para el modo de pantalla completa
|
|
borderColor = {0x00, 0x00, 0x00};
|
|
|
|
// Crea la textura donde se dibujan los graficos del juego
|
|
gameCanvas = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, gameCanvasWidth, gameCanvasHeight);
|
|
|
|
// Establece el modo de video
|
|
setVideoMode(options->video.mode);
|
|
|
|
// Muestra la ventana
|
|
SDL_ShowWindow(window);
|
|
}
|
|
|
|
// Destructor
|
|
Screen::~Screen()
|
|
{
|
|
delete notify;
|
|
SDL_DestroyTexture(gameCanvas);
|
|
}
|
|
|
|
// Limpia la pantalla
|
|
void Screen::clean(color_t 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, gameCanvas);
|
|
}
|
|
|
|
// Vuelca el contenido del renderizador en pantalla
|
|
void Screen::blit()
|
|
{
|
|
// Actualiza y dibuja el efecto de flash en la pantalla
|
|
doFlash();
|
|
|
|
// Atenua la pantalla
|
|
doAttenuate();
|
|
|
|
// Actualiza el contador de FPS
|
|
fpsCounter++;
|
|
|
|
// Muestra información por pantalla
|
|
displayInfo();
|
|
|
|
// Muestra las notificaciones
|
|
notify->render();
|
|
|
|
#ifdef NO_SHADERS
|
|
// Vuelve a dejar el renderizador en modo normal
|
|
SDL_SetRenderTarget(renderer, nullptr);
|
|
|
|
// Borra el contenido previo
|
|
SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, 0xFF);
|
|
SDL_RenderClear(renderer);
|
|
|
|
// Copia la textura de juego en el renderizador en la posición adecuada
|
|
SDL_RenderCopy(renderer, gameCanvas, nullptr, &dest);
|
|
|
|
// Muestra por pantalla el renderizador
|
|
SDL_RenderPresent(renderer);
|
|
#else
|
|
if (options->video.shaders)
|
|
{
|
|
shader::render();
|
|
}
|
|
else
|
|
{
|
|
// Vuelve a dejar el renderizador en modo normal
|
|
SDL_SetRenderTarget(renderer, nullptr);
|
|
|
|
// Borra el contenido previo
|
|
SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, 0xFF);
|
|
SDL_RenderClear(renderer);
|
|
|
|
// Copia la textura de juego en el renderizador en la posición adecuada
|
|
SDL_RenderCopy(renderer, gameCanvas, nullptr, &dest);
|
|
|
|
// Muestra por pantalla el renderizador
|
|
SDL_RenderPresent(renderer);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Establece el modo de video
|
|
void Screen::setVideoMode(int videoMode)
|
|
{
|
|
#ifdef ARCADE
|
|
videoMode = SCREEN_VIDEO_MODE_WINDOW;
|
|
// videoMode = SCREEN_VIDEO_MODE_FULLSCREEN;
|
|
#endif
|
|
|
|
// Si está activo el modo ventana quita el borde
|
|
if (videoMode == SCREEN_VIDEO_MODE_WINDOW)
|
|
{
|
|
// Aplica el modo de video
|
|
SDL_SetWindowFullscreen(window, 0);
|
|
|
|
#ifdef ARCADE
|
|
// Oculta el puntero
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
#else
|
|
// Muestra el puntero
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
#endif
|
|
|
|
windowWidth = gameCanvasWidth;
|
|
windowHeight = gameCanvasHeight;
|
|
dest = {0, 0, windowWidth, windowHeight};
|
|
|
|
// Modifica el tamaño de la ventana
|
|
SDL_SetWindowSize(window, windowWidth * options->video.window.size, windowHeight * options->video.window.size);
|
|
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
|
}
|
|
|
|
// Si está activo el modo de pantalla completa añade el borde
|
|
else if (videoMode == SCREEN_VIDEO_MODE_FULLSCREEN)
|
|
{
|
|
// Aplica el modo de video
|
|
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
#ifdef ARCADE
|
|
/*SDL_DisplayMode DM;
|
|
DM.w = 1024;
|
|
DM.h = 600;
|
|
DM.refresh_rate = 60;
|
|
if (!SDL_SetWindowDisplayMode(window, &DM))
|
|
{
|
|
std::cout << SDL_GetError() << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Changed windows display mode" << std::endl;
|
|
}*/
|
|
#endif
|
|
|
|
// Oculta el puntero
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
|
|
// Obten el alto y el ancho de la ventana
|
|
// SDL_GetWindowSize(window, &windowWidth, &windowHeight);
|
|
|
|
// Aplica el escalado al rectangulo donde se pinta la textura del juego
|
|
if (options->video.integerScale)
|
|
{
|
|
// Calcula el tamaño de la escala máxima
|
|
int scale = 0;
|
|
while (((gameCanvasWidth * (scale + 1)) <= windowWidth) && ((gameCanvasHeight * (scale + 1)) <= windowHeight))
|
|
{
|
|
scale++;
|
|
}
|
|
|
|
dest.w = gameCanvasWidth * scale;
|
|
dest.h = gameCanvasHeight * scale;
|
|
dest.x = (windowWidth - dest.w) / 2;
|
|
dest.y = (windowHeight - dest.h) / 2;
|
|
}
|
|
else if (options->video.keepAspect)
|
|
{
|
|
float ratio = (float)gameCanvasWidth / (float)gameCanvasHeight;
|
|
if ((windowWidth - gameCanvasWidth) >= (windowHeight - gameCanvasHeight))
|
|
{
|
|
dest.h = windowHeight;
|
|
dest.w = (int)((windowHeight * ratio) + 0.5f);
|
|
dest.x = (windowWidth - dest.w) / 2;
|
|
dest.y = (windowHeight - dest.h) / 2;
|
|
}
|
|
else
|
|
{
|
|
dest.w = windowWidth;
|
|
dest.h = (int)((windowWidth / ratio) + 0.5f);
|
|
dest.x = (windowWidth - dest.w) / 2;
|
|
dest.y = (windowHeight - dest.h) / 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// dest = {0, 0, windowWidth, windowHeight};
|
|
dest = {0, 0, gameCanvasWidth, gameCanvasHeight};
|
|
}
|
|
}
|
|
|
|
#ifdef NO_SHADERS
|
|
// El renderizador pasa a tener el tamaño de la pantalla en fullscreen o del juego en modo ventana
|
|
SDL_RenderSetLogicalSize(renderer, windowWidth, windowHeight);
|
|
#else
|
|
// Reinicia los shaders
|
|
if (options->video.shaders)
|
|
{
|
|
std::ifstream f(asset->get("crtpi.glsl").c_str());
|
|
std::string source((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
|
|
|
shader::init(window, gameCanvas, source.c_str());
|
|
}
|
|
|
|
// Modifica el tamaño del renderizador
|
|
else
|
|
{
|
|
// El renderizador pasa a tener el tamaño de la pantalla en fullscreen o del juego en modo ventana
|
|
// SDL_RenderSetLogicalSize(renderer, windowWidth, windowHeight);
|
|
SDL_RenderSetLogicalSize(renderer, gameCanvasWidth, gameCanvasHeight);
|
|
}
|
|
#endif
|
|
|
|
// Actualiza las opciones
|
|
options->video.mode = videoMode;
|
|
options->video.window.width = windowWidth;
|
|
options->video.window.height = windowHeight;
|
|
|
|
// Actualiza variables
|
|
shakeEffect.origin = dest.x;
|
|
}
|
|
|
|
// Camibia entre pantalla completa y ventana
|
|
void Screen::switchVideoMode()
|
|
{
|
|
options->video.mode = options->video.mode == SCREEN_VIDEO_MODE_WINDOW ? SCREEN_VIDEO_MODE_FULLSCREEN : SCREEN_VIDEO_MODE_WINDOW;
|
|
setVideoMode(options->video.mode);
|
|
}
|
|
|
|
// Cambia el tamaño de la ventana
|
|
void Screen::setWindowSize(int size)
|
|
{
|
|
options->video.window.size = size;
|
|
setVideoMode(SCREEN_VIDEO_MODE_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(SCREEN_VIDEO_MODE_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, 4);
|
|
setVideoMode(SCREEN_VIDEO_MODE_WINDOW);
|
|
}
|
|
|
|
// Cambia el color del borde
|
|
void Screen::setBorderColor(color_t color)
|
|
{
|
|
borderColor = 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()
|
|
{
|
|
updateShake();
|
|
notify->update();
|
|
updateFPS();
|
|
}
|
|
|
|
// Comprueba las entradas
|
|
void Screen::checkInput()
|
|
{
|
|
#ifndef ARCADE
|
|
if (input->checkInput(input_window_fullscreen, INPUT_DO_NOT_ALLOW_REPEAT))
|
|
{
|
|
switchVideoMode();
|
|
const std::string mode = options->video.mode == SCREEN_VIDEO_MODE_WINDOW ? "Window" : "Fullscreen";
|
|
showNotification(mode + " mode");
|
|
}
|
|
|
|
else if (input->checkInput(input_window_dec_size, INPUT_DO_NOT_ALLOW_REPEAT))
|
|
{
|
|
decWindowSize();
|
|
const std::string size = std::to_string(options->video.window.size);
|
|
showNotification("Window size x" + size);
|
|
}
|
|
|
|
else if (input->checkInput(input_window_inc_size, INPUT_DO_NOT_ALLOW_REPEAT))
|
|
{
|
|
incWindowSize();
|
|
const std::string size = std::to_string(options->video.window.size);
|
|
showNotification("Window size x" + size);
|
|
}
|
|
#endif
|
|
|
|
if (input->checkInput(input_video_shaders, INPUT_DO_NOT_ALLOW_REPEAT))
|
|
{
|
|
switchShaders();
|
|
}
|
|
|
|
else if (input->checkInput(input_showinfo, INPUT_DO_NOT_ALLOW_REPEAT))
|
|
{
|
|
showInfo = !showInfo;
|
|
}
|
|
|
|
// Comprueba el botón de SERVICE
|
|
switch (checkServiceButton(input))
|
|
{
|
|
case SERVICE_SHADERS:
|
|
switchShaders();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Agita la pantalla
|
|
void Screen::shake()
|
|
{
|
|
// Si ya hay un shake effect en marcha no se pilla el origen, solo se renuevan los contadores
|
|
if (shakeEffect.remaining == 0)
|
|
{
|
|
shakeEffect.origin = dest.x;
|
|
}
|
|
|
|
shakeEffect.remaining = shakeEffect.lenght;
|
|
shakeEffect.counter = shakeEffect.delay;
|
|
}
|
|
|
|
// Actualiza la logica para agitar la pantalla
|
|
void Screen::updateShake()
|
|
{
|
|
if (shakeEffect.remaining > 0)
|
|
{
|
|
if (shakeEffect.counter > 0)
|
|
{
|
|
shakeEffect.counter--;
|
|
}
|
|
else
|
|
{
|
|
shakeEffect.counter = shakeEffect.delay;
|
|
const int desp = shakeEffect.remaining % 2 == 0 ? shakeEffect.desp * (-1) : shakeEffect.desp;
|
|
dest.x = shakeEffect.origin + desp;
|
|
shakeEffect.remaining--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dest.x = shakeEffect.origin;
|
|
}
|
|
}
|
|
|
|
// Pone la pantalla de color
|
|
void Screen::flash(color_t color, int lenght)
|
|
{
|
|
flashEffect.enabled = true;
|
|
flashEffect.counter = 0;
|
|
flashEffect.lenght = lenght;
|
|
flashEffect.color = color;
|
|
}
|
|
|
|
// Actualiza y dibuja el efecto de flash en la pantalla
|
|
void Screen::doFlash()
|
|
{
|
|
if (flashEffect.enabled)
|
|
{
|
|
// Dibuja el color del flash en la textura
|
|
SDL_Texture *temp = SDL_GetRenderTarget(renderer);
|
|
SDL_SetRenderTarget(renderer, gameCanvas);
|
|
SDL_SetRenderDrawColor(renderer, flashEffect.color.r, flashEffect.color.g, flashEffect.color.b, 0xFF);
|
|
SDL_RenderClear(renderer);
|
|
SDL_SetRenderTarget(renderer, temp);
|
|
|
|
// Actualiza la lógica del efecto
|
|
flashEffect.counter < flashEffect.lenght ? flashEffect.counter++ : flashEffect.enabled = false;
|
|
}
|
|
}
|
|
|
|
// Atenua la pantalla
|
|
void Screen::doAttenuate()
|
|
{
|
|
if (attenuateEffect)
|
|
{
|
|
SDL_Texture *temp = SDL_GetRenderTarget(renderer);
|
|
SDL_SetRenderTarget(renderer, gameCanvas);
|
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 64);
|
|
SDL_RenderFillRect(renderer, nullptr);
|
|
SDL_SetRenderTarget(renderer, temp);
|
|
}
|
|
}
|
|
|
|
// Activa/desactiva los shaders
|
|
void Screen::switchShaders()
|
|
{
|
|
options->video.shaders = !options->video.shaders;
|
|
setVideoMode(options->video.mode);
|
|
const std::string value = options->video.shaders ? "on" : "off";
|
|
showNotification("Shaders " + value);
|
|
}
|
|
|
|
// Atenua la pantalla
|
|
void Screen::attenuate(bool value)
|
|
{
|
|
attenuateEffect = value;
|
|
}
|
|
|
|
// Muestra una notificación de texto por pantalla;
|
|
void Screen::showNotification(std::string text1, std::string text2, int icon)
|
|
{
|
|
notify->showText(text1, text2, icon);
|
|
}
|
|
|
|
// Obtiene el puntero al renderizador
|
|
SDL_Renderer *Screen::getRenderer()
|
|
{
|
|
return renderer;
|
|
}
|
|
|
|
// Calcula los frames por segundo
|
|
void Screen::updateFPS()
|
|
{
|
|
if (SDL_GetTicks() - fpsTicks > 1000)
|
|
{
|
|
fpsTicks = SDL_GetTicks();
|
|
fps = fpsCounter;
|
|
fpsCounter = 0;
|
|
}
|
|
}
|
|
|
|
// Muestra información por pantalla
|
|
void Screen::displayInfo()
|
|
{
|
|
if (showInfo)
|
|
{
|
|
// FPS
|
|
const std::string fpstext = std::to_string(fps) + " FPS";
|
|
dbg_print(gameCanvasWidth - fpstext.length() * 8, 0, fpstext.c_str(), 255, 255, 0);
|
|
|
|
// Resolution
|
|
dbg_print(0, 0, infoResolution.c_str(), 255, 255, 0);
|
|
}
|
|
} |