#include "screen.h" #include #include #include #include #ifndef NO_SHADERS #include "jshader.h" #endif #include "dbgtxt.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; borderWidth = options->video.border.width * 2; borderHeight = options->video.border.height * 2; 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; // 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(); // Pinta las notificaciones notify->render(); // Pinta el contador de FPS fpsCounter++; dbg_print(0, 0, std::to_string(fps).c_str(), 255, 255, 255); #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) { // Si está activo el modo ventana quita el borde if (videoMode == VIDEO_MODE_WINDOW) { // Aplica el modo de video SDL_SetWindowFullscreen(window, 0); // Muestra el puntero SDL_ShowCursor(SDL_ENABLE); if (options->video.border.enabled) { windowWidth = gameCanvasWidth + borderWidth; windowHeight = gameCanvasHeight + borderHeight; dest = {0 + (borderWidth / 2), 0 + (borderHeight / 2), gameCanvasWidth, gameCanvasHeight}; } else { windowWidth = gameCanvasWidth; windowHeight = gameCanvasHeight; dest = {0, 0, gameCanvasWidth, gameCanvasHeight}; } // 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 == VIDEO_MODE_FULLSCREEN) { // Aplica el modo de video SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); // 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.w = windowWidth; dest.h = windowHeight; dest.x = dest.y = 0; } } #ifdef NO_SHADERS 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(f)), std::istreambuf_iterator()); shader::init(window, gameCanvas, source.c_str()); } // Modifica el tamaño del renderizador else { SDL_RenderSetLogicalSize(renderer, windowWidth, windowHeight); } #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 == VIDEO_MODE_WINDOW ? VIDEO_MODE_FULLSCREEN : 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(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(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(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); } // Establece el tamaño del borde void Screen::setBorderWidth(int s) { options->video.border.width = s; } // Establece el tamaño del borde void Screen::setBorderHeight(int s) { options->video.border.height = s; } // Establece si se ha de ver el borde en el modo ventana void Screen::setBorderEnabled(bool value) { options->video.border.enabled = value; } // Cambia entre borde visible y no visible void Screen::switchBorder() { options->video.border.enabled = !options->video.border.enabled; setVideoMode(VIDEO_MODE_WINDOW); } // Actualiza la lógica de la clase void Screen::update() { updateShake(); notify->update(); updateFPS(); } // Comprueba las entradas void Screen::checkInput() { if (input->checkInput(input_window_fullscreen, DO_NOT_ALLOW_REPEAT)) { switchVideoMode(); const std::string mode = options->video.mode == VIDEO_MODE_WINDOW ? "Window" : "Fullscreen"; showNotification(mode + " mode"); } else if (input->checkInput(input_window_dec_size, 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, DO_NOT_ALLOW_REPEAT)) { incWindowSize(); const std::string size = std::to_string(options->video.window.size); showNotification("Window size x" + size); } else if (input->checkInput(input_video_shaders, DO_NOT_ALLOW_REPEAT)) { switchShaders(); const std::string value = options->video.shaders ? "on" : "off"; showNotification("Shaders " + value); } } // 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); } // 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; } }