diff --git a/README.md b/README.md index df616ea..5903315 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ El joc permet tant l'ús del teclat com d'un comandament. Les tecles per a jugar - **Tecla F1**: Disminueix la mida de la finestra. - **Tecla F2**: Augmenta la mida de la finestra. - **Tecla F3**: Alterna entre el mode de pantalla completa i el mode finestra. +- **Tecla F4**: Activa o desactiva els shaders - **Tecla F5**: Canvia la paleta de colors del joc. - **Tecla B**: Activa o desactiva el marge de colors en mode finestra. diff --git a/data/shaders/crtpi_192.glsl b/data/shaders/crtpi_192.glsl new file mode 100644 index 0000000..3f128f7 --- /dev/null +++ b/data/shaders/crtpi_192.glsl @@ -0,0 +1,234 @@ +/* + crt-pi - A Raspberry Pi friendly CRT shader. + + Copyright (C) 2015-2016 davej + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + +Notes: + +This shader is designed to work well on Raspberry Pi GPUs (i.e. 1080P @ 60Hz on a game with a 4:3 aspect ratio). It pushes the Pi's GPU hard and enabling some features will slow it down so that it is no longer able to match 1080P @ 60Hz. You will need to overclock your Pi to the fastest setting in raspi-config to get the best results from this shader: 'Pi2' for Pi2 and 'Turbo' for original Pi and Pi Zero. Note: Pi2s are slower at running the shader than other Pis, this seems to be down to Pi2s lower maximum memory speed. Pi2s don't quite manage 1080P @ 60Hz - they drop about 1 in 1000 frames. You probably won't notice this, but if you do, try enabling FAKE_GAMMA. + +SCANLINES enables scanlines. You'll almost certainly want to use it with MULTISAMPLE to reduce moire effects. SCANLINE_WEIGHT defines how wide scanlines are (it is an inverse value so a higher number = thinner lines). SCANLINE_GAP_BRIGHTNESS defines how dark the gaps between the scan lines are. Darker gaps between scan lines make moire effects more likely. + +GAMMA enables gamma correction using the values in INPUT_GAMMA and OUTPUT_GAMMA. FAKE_GAMMA causes it to ignore the values in INPUT_GAMMA and OUTPUT_GAMMA and approximate gamma correction in a way which is faster than true gamma whilst still looking better than having none. You must have GAMMA defined to enable FAKE_GAMMA. + +CURVATURE distorts the screen by CURVATURE_X and CURVATURE_Y. Curvature slows things down a lot. + +By default the shader uses linear blending horizontally. If you find this too blury, enable SHARPER. + +BLOOM_FACTOR controls the increase in width for bright scanlines. + +MASK_TYPE defines what, if any, shadow mask to use. MASK_BRIGHTNESS defines how much the mask type darkens the screen. + +*/ + +#pragma parameter CURVATURE_X "Screen curvature - horizontal" 0.10 0.0 1.0 0.01 +#pragma parameter CURVATURE_Y "Screen curvature - vertical" 0.15 0.0 1.0 0.01 +#pragma parameter MASK_BRIGHTNESS "Mask brightness" 0.70 0.0 1.0 0.01 +#pragma parameter SCANLINE_WEIGHT "Scanline weight" 6.0 0.0 15.0 0.1 +#pragma parameter SCANLINE_GAP_BRIGHTNESS "Scanline gap brightness" 0.12 0.0 1.0 0.01 +#pragma parameter BLOOM_FACTOR "Bloom factor" 1.5 0.0 5.0 0.01 +#pragma parameter INPUT_GAMMA "Input gamma" 2.4 0.0 5.0 0.01 +#pragma parameter OUTPUT_GAMMA "Output gamma" 2.2 0.0 5.0 0.01 + +// Haven't put these as parameters as it would slow the code down. +#define SCANLINES +#define MULTISAMPLE +#define GAMMA +//#define FAKE_GAMMA +//#define CURVATURE +//#define SHARPER +// MASK_TYPE: 0 = none, 1 = green/magenta, 2 = trinitron(ish) +#define MASK_TYPE 2 + + +#ifdef GL_ES +#define COMPAT_PRECISION mediump +precision mediump float; +#else +#define COMPAT_PRECISION +#endif + +#ifdef PARAMETER_UNIFORM +uniform COMPAT_PRECISION float CURVATURE_X; +uniform COMPAT_PRECISION float CURVATURE_Y; +uniform COMPAT_PRECISION float MASK_BRIGHTNESS; +uniform COMPAT_PRECISION float SCANLINE_WEIGHT; +uniform COMPAT_PRECISION float SCANLINE_GAP_BRIGHTNESS; +uniform COMPAT_PRECISION float BLOOM_FACTOR; +uniform COMPAT_PRECISION float INPUT_GAMMA; +uniform COMPAT_PRECISION float OUTPUT_GAMMA; +#else +#define CURVATURE_X 0.05 +#define CURVATURE_Y 0.1 +#define MASK_BRIGHTNESS 0.80 +#define SCANLINE_WEIGHT 6.0 +#define SCANLINE_GAP_BRIGHTNESS 0.12 +#define BLOOM_FACTOR 3.5 +#define INPUT_GAMMA 2.4 +#define OUTPUT_GAMMA 2.2 +#endif + +/* COMPATIBILITY + - GLSL compilers +*/ + +//uniform vec2 TextureSize; +#if defined(CURVATURE) +varying vec2 screenScale; +#endif +varying vec2 TEX0; +varying float filterWidth; + +#if defined(VERTEX) +//uniform mat4 MVPMatrix; +//attribute vec4 VertexCoord; +//attribute vec2 TexCoord; +//uniform vec2 InputSize; +//uniform vec2 OutputSize; + +void main() +{ +#if defined(CURVATURE) + screenScale = vec2(1.0, 1.0); //TextureSize / InputSize; +#endif + filterWidth = (768.0 / 192.0) / 3.0; + TEX0 = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} +#elif defined(FRAGMENT) + +uniform sampler2D Texture; + +#if defined(CURVATURE) +vec2 Distort(vec2 coord) +{ + vec2 CURVATURE_DISTORTION = vec2(CURVATURE_X, CURVATURE_Y); + // Barrel distortion shrinks the display area a bit, this will allow us to counteract that. + vec2 barrelScale = 1.0 - (0.23 * CURVATURE_DISTORTION); + coord *= screenScale; + coord -= vec2(0.5); + float rsq = coord.x * coord.x + coord.y * coord.y; + coord += coord * (CURVATURE_DISTORTION * rsq); + coord *= barrelScale; + if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5) + coord = vec2(-1.0); // If out of bounds, return an invalid value. + else + { + coord += vec2(0.5); + coord /= screenScale; + } + + return coord; +} +#endif + +float CalcScanLineWeight(float dist) +{ + return max(1.0-dist*dist*SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS); +} + +float CalcScanLine(float dy) +{ + float scanLineWeight = CalcScanLineWeight(dy); +#if defined(MULTISAMPLE) + scanLineWeight += CalcScanLineWeight(dy-filterWidth); + scanLineWeight += CalcScanLineWeight(dy+filterWidth); + scanLineWeight *= 0.3333333; +#endif + return scanLineWeight; +} + +void main() +{ + vec2 TextureSize = vec2(256.0, 192.0); +#if defined(CURVATURE) + vec2 texcoord = Distort(TEX0); + if (texcoord.x < 0.0) + gl_FragColor = vec4(0.0); + else +#else + vec2 texcoord = TEX0; +#endif + { + vec2 texcoordInPixels = texcoord * TextureSize; +#if defined(SHARPER) + vec2 tempCoord = floor(texcoordInPixels) + 0.5; + vec2 coord = tempCoord / TextureSize; + vec2 deltas = texcoordInPixels - tempCoord; + float scanLineWeight = CalcScanLine(deltas.y); + vec2 signs = sign(deltas); + deltas.x *= 2.0; + deltas = deltas * deltas; + deltas.y = deltas.y * deltas.y; + deltas.x *= 0.5; + deltas.y *= 8.0; + deltas /= TextureSize; + deltas *= signs; + vec2 tc = coord + deltas; +#else + float tempY = floor(texcoordInPixels.y) + 0.5; + float yCoord = tempY / TextureSize.y; + float dy = texcoordInPixels.y - tempY; + float scanLineWeight = CalcScanLine(dy); + float signY = sign(dy); + dy = dy * dy; + dy = dy * dy; + dy *= 8.0; + dy /= TextureSize.y; + dy *= signY; + vec2 tc = vec2(texcoord.x, yCoord + dy); +#endif + + vec3 colour = texture2D(Texture, tc).rgb; + +#if defined(SCANLINES) +#if defined(GAMMA) +#if defined(FAKE_GAMMA) + colour = colour * colour; +#else + colour = pow(colour, vec3(INPUT_GAMMA)); +#endif +#endif + scanLineWeight *= BLOOM_FACTOR; + colour *= scanLineWeight; + +#if defined(GAMMA) +#if defined(FAKE_GAMMA) + colour = sqrt(colour); +#else + colour = pow(colour, vec3(1.0/OUTPUT_GAMMA)); +#endif +#endif +#endif +#if MASK_TYPE == 0 + gl_FragColor = vec4(colour, 1.0); +#else +#if MASK_TYPE == 1 + float whichMask = fract((gl_FragCoord.x*1.0001) * 0.5); + vec3 mask; + if (whichMask < 0.5) + mask = vec3(MASK_BRIGHTNESS, 1.0, MASK_BRIGHTNESS); + else + mask = vec3(1.0, MASK_BRIGHTNESS, 1.0); +#elif MASK_TYPE == 2 + float whichMask = fract((gl_FragCoord.x*1.0001) * 0.3333333); + vec3 mask = vec3(MASK_BRIGHTNESS, MASK_BRIGHTNESS, MASK_BRIGHTNESS); + if (whichMask < 0.3333333) + mask.x = 1.0; + else if (whichMask < 0.6666666) + mask.y = 1.0; + else + mask.z = 1.0; +#endif + + gl_FragColor = vec4(colour * mask, 1.0); +#endif + } +} +#endif diff --git a/data/shaders/crtpi_240.glsl b/data/shaders/crtpi_240.glsl new file mode 100644 index 0000000..068a6f1 --- /dev/null +++ b/data/shaders/crtpi_240.glsl @@ -0,0 +1,234 @@ +/* + crt-pi - A Raspberry Pi friendly CRT shader. + + Copyright (C) 2015-2016 davej + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + +Notes: + +This shader is designed to work well on Raspberry Pi GPUs (i.e. 1080P @ 60Hz on a game with a 4:3 aspect ratio). It pushes the Pi's GPU hard and enabling some features will slow it down so that it is no longer able to match 1080P @ 60Hz. You will need to overclock your Pi to the fastest setting in raspi-config to get the best results from this shader: 'Pi2' for Pi2 and 'Turbo' for original Pi and Pi Zero. Note: Pi2s are slower at running the shader than other Pis, this seems to be down to Pi2s lower maximum memory speed. Pi2s don't quite manage 1080P @ 60Hz - they drop about 1 in 1000 frames. You probably won't notice this, but if you do, try enabling FAKE_GAMMA. + +SCANLINES enables scanlines. You'll almost certainly want to use it with MULTISAMPLE to reduce moire effects. SCANLINE_WEIGHT defines how wide scanlines are (it is an inverse value so a higher number = thinner lines). SCANLINE_GAP_BRIGHTNESS defines how dark the gaps between the scan lines are. Darker gaps between scan lines make moire effects more likely. + +GAMMA enables gamma correction using the values in INPUT_GAMMA and OUTPUT_GAMMA. FAKE_GAMMA causes it to ignore the values in INPUT_GAMMA and OUTPUT_GAMMA and approximate gamma correction in a way which is faster than true gamma whilst still looking better than having none. You must have GAMMA defined to enable FAKE_GAMMA. + +CURVATURE distorts the screen by CURVATURE_X and CURVATURE_Y. Curvature slows things down a lot. + +By default the shader uses linear blending horizontally. If you find this too blury, enable SHARPER. + +BLOOM_FACTOR controls the increase in width for bright scanlines. + +MASK_TYPE defines what, if any, shadow mask to use. MASK_BRIGHTNESS defines how much the mask type darkens the screen. + +*/ + +#pragma parameter CURVATURE_X "Screen curvature - horizontal" 0.10 0.0 1.0 0.01 +#pragma parameter CURVATURE_Y "Screen curvature - vertical" 0.15 0.0 1.0 0.01 +#pragma parameter MASK_BRIGHTNESS "Mask brightness" 0.70 0.0 1.0 0.01 +#pragma parameter SCANLINE_WEIGHT "Scanline weight" 6.0 0.0 15.0 0.1 +#pragma parameter SCANLINE_GAP_BRIGHTNESS "Scanline gap brightness" 0.12 0.0 1.0 0.01 +#pragma parameter BLOOM_FACTOR "Bloom factor" 1.5 0.0 5.0 0.01 +#pragma parameter INPUT_GAMMA "Input gamma" 2.4 0.0 5.0 0.01 +#pragma parameter OUTPUT_GAMMA "Output gamma" 2.2 0.0 5.0 0.01 + +// Haven't put these as parameters as it would slow the code down. +#define SCANLINES +#define MULTISAMPLE +#define GAMMA +//#define FAKE_GAMMA +//#define CURVATURE +//#define SHARPER +// MASK_TYPE: 0 = none, 1 = green/magenta, 2 = trinitron(ish) +#define MASK_TYPE 2 + + +#ifdef GL_ES +#define COMPAT_PRECISION mediump +precision mediump float; +#else +#define COMPAT_PRECISION +#endif + +#ifdef PARAMETER_UNIFORM +uniform COMPAT_PRECISION float CURVATURE_X; +uniform COMPAT_PRECISION float CURVATURE_Y; +uniform COMPAT_PRECISION float MASK_BRIGHTNESS; +uniform COMPAT_PRECISION float SCANLINE_WEIGHT; +uniform COMPAT_PRECISION float SCANLINE_GAP_BRIGHTNESS; +uniform COMPAT_PRECISION float BLOOM_FACTOR; +uniform COMPAT_PRECISION float INPUT_GAMMA; +uniform COMPAT_PRECISION float OUTPUT_GAMMA; +#else +#define CURVATURE_X 0.05 +#define CURVATURE_Y 0.1 +#define MASK_BRIGHTNESS 0.80 +#define SCANLINE_WEIGHT 6.0 +#define SCANLINE_GAP_BRIGHTNESS 0.12 +#define BLOOM_FACTOR 3.5 +#define INPUT_GAMMA 2.4 +#define OUTPUT_GAMMA 2.2 +#endif + +/* COMPATIBILITY + - GLSL compilers +*/ + +//uniform vec2 TextureSize; +#if defined(CURVATURE) +varying vec2 screenScale; +#endif +varying vec2 TEX0; +varying float filterWidth; + +#if defined(VERTEX) +//uniform mat4 MVPMatrix; +//attribute vec4 VertexCoord; +//attribute vec2 TexCoord; +//uniform vec2 InputSize; +//uniform vec2 OutputSize; + +void main() +{ +#if defined(CURVATURE) + screenScale = vec2(1.0, 1.0); //TextureSize / InputSize; +#endif + filterWidth = (768.0 / 240.0) / 3.0; + TEX0 = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} +#elif defined(FRAGMENT) + +uniform sampler2D Texture; + +#if defined(CURVATURE) +vec2 Distort(vec2 coord) +{ + vec2 CURVATURE_DISTORTION = vec2(CURVATURE_X, CURVATURE_Y); + // Barrel distortion shrinks the display area a bit, this will allow us to counteract that. + vec2 barrelScale = 1.0 - (0.23 * CURVATURE_DISTORTION); + coord *= screenScale; + coord -= vec2(0.5); + float rsq = coord.x * coord.x + coord.y * coord.y; + coord += coord * (CURVATURE_DISTORTION * rsq); + coord *= barrelScale; + if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5) + coord = vec2(-1.0); // If out of bounds, return an invalid value. + else + { + coord += vec2(0.5); + coord /= screenScale; + } + + return coord; +} +#endif + +float CalcScanLineWeight(float dist) +{ + return max(1.0-dist*dist*SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS); +} + +float CalcScanLine(float dy) +{ + float scanLineWeight = CalcScanLineWeight(dy); +#if defined(MULTISAMPLE) + scanLineWeight += CalcScanLineWeight(dy-filterWidth); + scanLineWeight += CalcScanLineWeight(dy+filterWidth); + scanLineWeight *= 0.3333333; +#endif + return scanLineWeight; +} + +void main() +{ + vec2 TextureSize = vec2(320.0, 240.0); +#if defined(CURVATURE) + vec2 texcoord = Distort(TEX0); + if (texcoord.x < 0.0) + gl_FragColor = vec4(0.0); + else +#else + vec2 texcoord = TEX0; +#endif + { + vec2 texcoordInPixels = texcoord * TextureSize; +#if defined(SHARPER) + vec2 tempCoord = floor(texcoordInPixels) + 0.5; + vec2 coord = tempCoord / TextureSize; + vec2 deltas = texcoordInPixels - tempCoord; + float scanLineWeight = CalcScanLine(deltas.y); + vec2 signs = sign(deltas); + deltas.x *= 2.0; + deltas = deltas * deltas; + deltas.y = deltas.y * deltas.y; + deltas.x *= 0.5; + deltas.y *= 8.0; + deltas /= TextureSize; + deltas *= signs; + vec2 tc = coord + deltas; +#else + float tempY = floor(texcoordInPixels.y) + 0.5; + float yCoord = tempY / TextureSize.y; + float dy = texcoordInPixels.y - tempY; + float scanLineWeight = CalcScanLine(dy); + float signY = sign(dy); + dy = dy * dy; + dy = dy * dy; + dy *= 8.0; + dy /= TextureSize.y; + dy *= signY; + vec2 tc = vec2(texcoord.x, yCoord + dy); +#endif + + vec3 colour = texture2D(Texture, tc).rgb; + +#if defined(SCANLINES) +#if defined(GAMMA) +#if defined(FAKE_GAMMA) + colour = colour * colour; +#else + colour = pow(colour, vec3(INPUT_GAMMA)); +#endif +#endif + scanLineWeight *= BLOOM_FACTOR; + colour *= scanLineWeight; + +#if defined(GAMMA) +#if defined(FAKE_GAMMA) + colour = sqrt(colour); +#else + colour = pow(colour, vec3(1.0/OUTPUT_GAMMA)); +#endif +#endif +#endif +#if MASK_TYPE == 0 + gl_FragColor = vec4(colour, 1.0); +#else +#if MASK_TYPE == 1 + float whichMask = fract((gl_FragCoord.x*1.0001) * 0.5); + vec3 mask; + if (whichMask < 0.5) + mask = vec3(MASK_BRIGHTNESS, 1.0, MASK_BRIGHTNESS); + else + mask = vec3(1.0, MASK_BRIGHTNESS, 1.0); +#elif MASK_TYPE == 2 + float whichMask = fract((gl_FragCoord.x*1.0001) * 0.3333333); + vec3 mask = vec3(MASK_BRIGHTNESS, MASK_BRIGHTNESS, MASK_BRIGHTNESS); + if (whichMask < 0.3333333) + mask.x = 1.0; + else if (whichMask < 0.6666666) + mask.y = 1.0; + else + mask.z = 1.0; +#endif + + gl_FragColor = vec4(colour * mask, 1.0); +#endif + } +} +#endif diff --git a/source/director.cpp b/source/director.cpp index 6832ddd..1cfa05d 100644 --- a/source/director.cpp +++ b/source/director.cpp @@ -131,6 +131,7 @@ void Director::initOptions() options->videoMode = 0; options->windowSize = 3; options->filter = FILTER_NEAREST; + options->shaders = false; options->vSync = true; options->integerScale = true; options->keepAspect = true; @@ -373,6 +374,7 @@ bool Director::saveConfig() file << "filter=FILTER_LINEAL\n"; } + file << "shaders=" + boolToString(options->shaders) + "\n"; file << "vSync=" + boolToString(options->vSync) + "\n"; file << "integerScale=" + boolToString(options->integerScale) + "\n"; file << "keepAspect=" + boolToString(options->keepAspect) + "\n"; @@ -1106,6 +1108,11 @@ bool Director::setOptions(options_t *options, std::string var, std::string value } } + else if (var == "shaders") + { + options->shaders = stringToBool(value); + } + else if (var == "vSync") { options->vSync = stringToBool(value); @@ -1262,9 +1269,10 @@ void Director::initInput() input->bindKey(input_exit, SDL_SCANCODE_ESCAPE); input->bindKey(input_window_dec_size, SDL_SCANCODE_F1); input->bindKey(input_window_inc_size, SDL_SCANCODE_F2); - input->bindKey(input_window_fullscreen, SDL_SCANCODE_F3); - input->bindKey(input_swap_palette, SDL_SCANCODE_F5); - input->bindKey(input_switch_music, SDL_SCANCODE_M); + input->bindKey(input_toggle_videomode, SDL_SCANCODE_F3); + input->bindKey(input_toggle_shaders, SDL_SCANCODE_F4); + input->bindKey(input_toggle_palette, SDL_SCANCODE_F5); + input->bindKey(input_toggle_music, SDL_SCANCODE_M); input->bindKey(input_toggle_border, SDL_SCANCODE_B); // Mando - Movimiento @@ -1282,8 +1290,8 @@ void Director::initInput() input->bindGameControllerButton(input_pause, SDL_CONTROLLER_BUTTON_START); input->bindGameControllerButton(input_exit, SDL_CONTROLLER_BUTTON_BACK); #endif - input->bindGameControllerButton(input_swap_palette, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); - input->bindGameControllerButton(input_switch_music, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); + input->bindGameControllerButton(input_toggle_palette, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); + input->bindGameControllerButton(input_toggle_music, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); input->bindGameControllerButton(input_toggle_border, SDL_CONTROLLER_BUTTON_X); } @@ -1300,7 +1308,7 @@ bool Director::initSDL() bool success = true; // Inicializa SDL - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO) < 0) + if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { if (options->console) { @@ -1322,6 +1330,12 @@ bool Director::initSDL() } } + // Activa el render OpenGL + if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) + { + std::cout << "Warning: opengl not enabled!\n"; + } + // Crea la ventana int incW = 0; int incH = 0; @@ -1331,7 +1345,7 @@ bool Director::initSDL() incH = options->borderHeight * 2; } - window = SDL_CreateWindow(WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (options->gameWidth + incW) * options->windowSize, (options->gameHeight + incH) * options->windowSize, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI); + window = SDL_CreateWindow(WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (options->gameWidth + incW) * options->windowSize, (options->gameHeight + incH) * options->windowSize, SDL_WINDOW_HIDDEN); if (window == nullptr) { if (options->console) @@ -1343,8 +1357,6 @@ bool Director::initSDL() else { // Crea un renderizador para la ventana. El vsync se activa en funcion de las opciones - // Uint32 flags = SDL_RENDERER_SOFTWARE; - // Uint32 flags = SDL_RENDERER_ACCELERATED; Uint32 flags = 0; if (options->vSync) { @@ -1400,6 +1412,10 @@ bool Director::setFileList() asset->add(prefix + "/data/font/subatomic.png", t_font); asset->add(prefix + "/data/font/subatomic.txt", t_font); + // Shaders + asset->add(prefix + "/data/shaders/crtpi_192.glsl", t_data); + asset->add(prefix + "/data/shaders/crtpi_240.glsl", t_data); + // Datos asset->add(prefix + "/data/input/gamecontrollerdb.txt", t_data); diff --git a/source/enter_id.cpp b/source/enter_id.cpp index fb65413..0a2477a 100644 --- a/source/enter_id.cpp +++ b/source/enter_id.cpp @@ -197,7 +197,7 @@ void EnterID::render() text->writeDX(TXT_CENTER | TXT_COLOR, GAMECANVAS_CENTER_X, jailerIDPos, jailerID, 1, color); // Vuelca el contenido del renderizador en pantalla - screen->blit(); + screen->render(); } // Inicializa los textos diff --git a/source/gamestate_credits.cpp b/source/gamestate_credits.cpp index 6fa9205..4356bb7 100644 --- a/source/gamestate_credits.cpp +++ b/source/gamestate_credits.cpp @@ -93,12 +93,12 @@ void Credits::checkInput() else if (input->checkInput(input_toggle_border, REPEAT_FALSE)) { - screen->switchBorder(); + screen->toggleBorder(); } - else if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_videomode, REPEAT_FALSE)) { - screen->switchVideoMode(); + screen->toggleVideoMode(); } else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) @@ -111,7 +111,7 @@ void Credits::checkInput() screen->incWindowSize(); } - else if (input->checkInput(input_swap_palette, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_palette, REPEAT_FALSE)) { switchPalette(); } @@ -338,7 +338,7 @@ void Credits::render() } // Vuelca el contenido del renderizador en pantalla - screen->blit(); + screen->render(); } // Bucle para el logo del juego diff --git a/source/gamestate_demo.cpp b/source/gamestate_demo.cpp index 16b5f9b..9ed2ad6 100644 --- a/source/gamestate_demo.cpp +++ b/source/gamestate_demo.cpp @@ -87,13 +87,13 @@ void Demo::checkInput() else if (input->checkInput(input_toggle_border, REPEAT_FALSE)) { - screen->switchBorder(); + screen->toggleBorder(); reLoadTextures(); } - else if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_videomode, REPEAT_FALSE)) { - screen->switchVideoMode(); + screen->toggleVideoMode(); reLoadTextures(); } @@ -109,7 +109,7 @@ void Demo::checkInput() reLoadTextures(); } - else if (input->checkInput(input_swap_palette, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_palette, REPEAT_FALSE)) { switchPalette(); } @@ -170,7 +170,7 @@ void Demo::render() screen->renderFX(); // Actualiza la pantalla - screen->blit(); + screen->render(); } // Escribe el nombre de la pantalla diff --git a/source/gamestate_ending.cpp b/source/gamestate_ending.cpp index 182cda0..d6e5706 100644 --- a/source/gamestate_ending.cpp +++ b/source/gamestate_ending.cpp @@ -140,7 +140,7 @@ void Ending::render() // text->write(0, 0, std::to_string(counter)); // Vuelca el contenido del renderizador en pantalla - screen->blit(); + screen->render(); } // Comprueba el manejador de eventos @@ -170,12 +170,12 @@ void Ending::checkInput() else if (input->checkInput(input_toggle_border, REPEAT_FALSE)) { - screen->switchBorder(); + screen->toggleBorder(); } - else if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_videomode, REPEAT_FALSE)) { - screen->switchVideoMode(); + screen->toggleVideoMode(); } else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) @@ -188,7 +188,7 @@ void Ending::checkInput() screen->incWindowSize(); } - else if (input->checkInput(input_swap_palette, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_palette, REPEAT_FALSE)) { switchPalette(); } diff --git a/source/gamestate_ending2.cpp b/source/gamestate_ending2.cpp index ce323b8..3e48419 100644 --- a/source/gamestate_ending2.cpp +++ b/source/gamestate_ending2.cpp @@ -174,7 +174,7 @@ void Ending2::render() } // Vuelca el contenido del renderizador en pantalla - screen->blit(); + screen->render(); } // Comprueba el manejador de eventos @@ -204,12 +204,12 @@ void Ending2::checkInput() else if (input->checkInput(input_toggle_border, REPEAT_FALSE)) { - screen->switchBorder(); + screen->toggleBorder(); } - else if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_videomode, REPEAT_FALSE)) { - screen->switchVideoMode(); + screen->toggleVideoMode(); } else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) @@ -222,7 +222,7 @@ void Ending2::checkInput() screen->incWindowSize(); } - else if (input->checkInput(input_swap_palette, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_palette, REPEAT_FALSE)) { switchPalette(); } diff --git a/source/gamestate_game.cpp b/source/gamestate_game.cpp index 9529ed5..8972479 100644 --- a/source/gamestate_game.cpp +++ b/source/gamestate_game.cpp @@ -192,7 +192,7 @@ void Game::checkInput() section->name = SECTION_TITLE; } - else if (input->checkInput(input_switch_music, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_music, REPEAT_FALSE)) { board.music = !board.music; board.music ? JA_ResumeMusic() : JA_PauseMusic(); @@ -205,16 +205,21 @@ void Game::checkInput() else if (input->checkInput(input_toggle_border, REPEAT_FALSE)) { - screen->switchBorder(); + screen->toggleBorder(); reLoadTextures(); } - else if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_videomode, REPEAT_FALSE)) { - screen->switchVideoMode(); + screen->toggleVideoMode(); reLoadTextures(); } + else if (input->checkInput(input_toggle_shaders, REPEAT_FALSE)) + { + screen->toggleShaders(); + } + else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) { screen->decWindowSize(); @@ -227,7 +232,7 @@ void Game::checkInput() reLoadTextures(); } - else if (input->checkInput(input_swap_palette, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_palette, REPEAT_FALSE)) { switchPalette(); } @@ -314,7 +319,7 @@ void Game::render() #endif // Actualiza la pantalla - screen->blit(); + screen->render(); } #ifdef DEBUG diff --git a/source/gamestate_game_over.cpp b/source/gamestate_game_over.cpp index 124fb53..851b288 100644 --- a/source/gamestate_game_over.cpp +++ b/source/gamestate_game_over.cpp @@ -111,7 +111,7 @@ void GameOver::render() text->writeDX(TXT_CENTER | TXT_COLOR, GAMECANVAS_CENTER_X, y + 120, options->stats.worstNightmare, 1, color); // Vuelca el contenido del renderizador en pantalla - screen->blit(); + screen->render(); } // Comprueba el manejador de eventos @@ -141,12 +141,12 @@ void GameOver::checkInput() else if (input->checkInput(input_toggle_border, REPEAT_FALSE)) { - screen->switchBorder(); + screen->toggleBorder(); } - else if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_videomode, REPEAT_FALSE)) { - screen->switchVideoMode(); + screen->toggleVideoMode(); } else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) @@ -159,7 +159,7 @@ void GameOver::checkInput() screen->incWindowSize(); } - else if (input->checkInput(input_swap_palette, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_palette, REPEAT_FALSE)) { switchPalette(); } diff --git a/source/gamestate_loading_screen.cpp b/source/gamestate_loading_screen.cpp index 7d92874..9637630 100644 --- a/source/gamestate_loading_screen.cpp +++ b/source/gamestate_loading_screen.cpp @@ -101,12 +101,12 @@ void LoadingScreen::checkInput() else if (input->checkInput(input_toggle_border, REPEAT_FALSE)) { - screen->switchBorder(); + screen->toggleBorder(); } - else if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_videomode, REPEAT_FALSE)) { - screen->switchVideoMode(); + screen->toggleVideoMode(); } else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) @@ -119,7 +119,7 @@ void LoadingScreen::checkInput() screen->incWindowSize(); } - else if (input->checkInput(input_swap_palette, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_palette, REPEAT_FALSE)) { switchPalette(); } @@ -274,7 +274,7 @@ void LoadingScreen::render() renderLoad(); // Vuelca el contenido del renderizador en pantalla - screen->blit(); + screen->render(); } // Bucle para el logo del juego @@ -287,7 +287,7 @@ void LoadingScreen::run() // Limpia la pantalla screen->start(); screen->clean(); - screen->blit(); + screen->render(); while (section->name == SECTION_LOADING_SCREEN) { @@ -354,5 +354,5 @@ void LoadingScreen::recreateLoadingScreen() } // Vuelca el contenido del renderizador en pantalla - screen->blit(); + screen->render(); } \ No newline at end of file diff --git a/source/gamestate_logo.cpp b/source/gamestate_logo.cpp index a26fc69..376cedc 100644 --- a/source/gamestate_logo.cpp +++ b/source/gamestate_logo.cpp @@ -94,12 +94,12 @@ void Logo::checkInput() else if (input->checkInput(input_toggle_border, REPEAT_FALSE)) { - screen->switchBorder(); + screen->toggleBorder(); } - else if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_videomode, REPEAT_FALSE)) { - screen->switchVideoMode(); + screen->toggleVideoMode(); } else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) @@ -112,7 +112,7 @@ void Logo::checkInput() screen->incWindowSize(); } - else if (input->checkInput(input_swap_palette, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_palette, REPEAT_FALSE)) { switchPalette(); } @@ -294,7 +294,7 @@ void Logo::render() sprite2->render(); // Vuelca el contenido del renderizador en pantalla - screen->blit(); + screen->render(); } // Bucle para el logo del juego diff --git a/source/gamestate_title.cpp b/source/gamestate_title.cpp index 5515bec..bb5768e 100644 --- a/source/gamestate_title.cpp +++ b/source/gamestate_title.cpp @@ -171,16 +171,21 @@ void Title::checkInput() else if (input->checkInput(input_toggle_border, REPEAT_FALSE)) { - screen->switchBorder(); + screen->toggleBorder(); resource->reLoadTextures(); } - else if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_videomode, REPEAT_FALSE)) { - screen->switchVideoMode(); + screen->toggleVideoMode(); resource->reLoadTextures(); } + else if (input->checkInput(input_toggle_shaders, REPEAT_FALSE)) + { + screen->toggleShaders(); + } + else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) { screen->decWindowSize(); @@ -193,7 +198,7 @@ void Title::checkInput() resource->reLoadTextures(); } - else if (input->checkInput(input_swap_palette, REPEAT_FALSE)) + else if (input->checkInput(input_toggle_palette, REPEAT_FALSE)) { switchPalette(); } @@ -326,7 +331,7 @@ void Title::render() if (state == show_menu) { - // Dibuja la textura de fondo + // Dibuja la textura de fondo SDL_RenderCopy(renderer, bgTexture, nullptr, nullptr); // Dibuja la marquesina @@ -350,7 +355,7 @@ void Title::render() } // Vuelca el contenido del renderizador en pantalla - screen->blit(); + screen->render(); } // Bucle para el logo del juego @@ -438,10 +443,10 @@ void Title::fillTexture() sprite->render(); // Borra la firma - //const color_t coverColor = stringToColor(options->palette, "black"); - //SDL_SetRenderDrawColor(renderer, coverColor.r, coverColor.g, coverColor.b, 0xFF); - //SDL_Rect coverRect = {28, 11, 21, 5}; - //SDL_RenderFillRect(renderer, &coverRect); + // const color_t coverColor = stringToColor(options->palette, "black"); + // SDL_SetRenderDrawColor(renderer, coverColor.r, coverColor.g, coverColor.b, 0xFF); + // SDL_Rect coverRect = {28, 11, 21, 5}; + // SDL_RenderFillRect(renderer, &coverRect); // Escribe el texto en la textura const color_t textColor = stringToColor(options->palette, "green"); diff --git a/source/jail_engine/input.h b/source/jail_engine/input.h index d08aa2f..1ba3836 100644 --- a/source/jail_engine/input.h +++ b/source/jail_engine/input.h @@ -22,12 +22,13 @@ enum inputs_e // Inputs personalizados input_jump, - input_window_fullscreen, input_window_inc_size, input_window_dec_size, + input_toggle_videomode, input_toggle_border, - input_switch_music, - input_swap_palette, + input_toggle_music, + input_toggle_palette, + input_toggle_shaders, // Input obligatorio input_number_of_inputs diff --git a/source/jail_engine/jail_shader.cpp b/source/jail_engine/jail_shader.cpp new file mode 100644 index 0000000..44d75d3 --- /dev/null +++ b/source/jail_engine/jail_shader.cpp @@ -0,0 +1,251 @@ +#ifndef NO_SHADERS + +#include "jail_shader.h" +#include // para SDL_Point +#include // para NULL, free, malloc, exit +#include // para strncmp +#include // para basic_ostream, char_traits, operator<< + +#ifdef __APPLE__ +#include "CoreFoundation/CoreFoundation.h" +#include + +#if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 +#include +#else +#include +#endif //! ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 +#else +#include +#endif + +namespace shader +{ + SDL_Window *win = nullptr; + SDL_Renderer *renderer = nullptr; + GLuint programId = 0; + SDL_Texture *backBuffer = nullptr; + SDL_Point win_size = {320 * 4, 256 * 4}; + SDL_Point tex_size = {320, 256}; + bool usingOpenGL; + +#ifndef __APPLE__ + + // I'm avoiding the use of GLEW or some extensions handler, but that + // doesn't mean you should... + PFNGLCREATESHADERPROC glCreateShader; + PFNGLSHADERSOURCEPROC glShaderSource; + PFNGLCOMPILESHADERPROC glCompileShader; + PFNGLGETSHADERIVPROC glGetShaderiv; + PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; + PFNGLDELETESHADERPROC glDeleteShader; + PFNGLATTACHSHADERPROC glAttachShader; + PFNGLCREATEPROGRAMPROC glCreateProgram; + PFNGLLINKPROGRAMPROC glLinkProgram; + PFNGLVALIDATEPROGRAMPROC glValidateProgram; + PFNGLGETPROGRAMIVPROC glGetProgramiv; + PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; + PFNGLUSEPROGRAMPROC glUseProgram; + + bool initGLExtensions() + { + glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader"); + glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource"); + glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader"); + glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv"); + glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog"); + glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader"); + glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader"); + glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram"); + glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram"); + glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram"); + glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv"); + glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog"); + glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram"); + + return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv && + glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram && + glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog && + glUseProgram; + } + +#endif + + GLuint compileShader(const char *source, GLuint shaderType) + { + // Create ID for shader + GLuint result = glCreateShader(shaderType); + // Add define depending on shader type + const char *sources[2] = {shaderType == GL_VERTEX_SHADER ? "#define VERTEX\n" : "#define FRAGMENT\n", source}; + // Define shader text + glShaderSource(result, 2, sources, NULL); + // Compile shader + glCompileShader(result); + + // Check vertex shader for errors + GLint shaderCompiled = GL_FALSE; + glGetShaderiv(result, GL_COMPILE_STATUS, &shaderCompiled); + if (shaderCompiled != GL_TRUE) + { + std::cout << "Error en la compilación: " << result << "!" << std::endl; + GLint logLength; + glGetShaderiv(result, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) + { + GLchar *log = (GLchar *)malloc(logLength); + glGetShaderInfoLog(result, logLength, &logLength, log); + std::cout << "Shader compile log:" << log << std::endl; + free(log); + } + glDeleteShader(result); + result = 0; + // } else { + // std::cout << "Shader compilado correctamente. Id = " << result << std::endl; + } + return result; + } + + GLuint compileProgram(const char *vertexShaderSource, const char *fragmentShaderSource) + { + GLuint programId = 0; + GLuint vtxShaderId, fragShaderId; + + programId = glCreateProgram(); + + vtxShaderId = compileShader(vertexShaderSource, GL_VERTEX_SHADER); + fragShaderId = compileShader(fragmentShaderSource ? fragmentShaderSource : vertexShaderSource, GL_FRAGMENT_SHADER); + + if (vtxShaderId && fragShaderId) + { + // Associate shader with program + glAttachShader(programId, vtxShaderId); + glAttachShader(programId, fragShaderId); + glLinkProgram(programId); + glValidateProgram(programId); + + // Check the status of the compile/link + GLint logLen; + glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &logLen); + if (logLen > 0) + { + char *log = (char *)malloc(logLen * sizeof(char)); + // Show any errors as appropriate + glGetProgramInfoLog(programId, logLen, &logLen, log); + std::cout << "Prog Info Log: " << std::endl + << log << std::endl; + free(log); + } + } + if (vtxShaderId) + { + glDeleteShader(vtxShaderId); + } + if (fragShaderId) + { + glDeleteShader(fragShaderId); + } + return programId; + } + + const bool init(SDL_Window *win, SDL_Texture *backBuffer, const char *vertexShader, const char *fragmentShader) + { + shader::win = win; + shader::renderer = SDL_GetRenderer(win); + shader::backBuffer = backBuffer; + SDL_GetWindowSize(win, &win_size.x, &win_size.y); + int access; + SDL_QueryTexture(backBuffer, NULL, &access, &tex_size.x, &tex_size.y); + if (access != SDL_TEXTUREACCESS_TARGET) + { + std::cout << "ERROR FATAL: La textura per al render ha de tindre SDL_TEXTUREACCESS_TARGET definit." << std::endl; + exit(1); + } + + SDL_RendererInfo rendererInfo; + SDL_GetRendererInfo(renderer, &rendererInfo); + + if (!strncmp(rendererInfo.name, "opengl", 6)) + { + // std::cout << "Es OpenGL!" << std::endl; +#ifndef __APPLE__ + if (!initGLExtensions()) + { + std::cout << "WARNING: No s'han pogut inicialitzar les extensions d'OpenGL!" << std::endl; + usingOpenGL = false; + return false; + } +#endif + // Compilar el shader y dejarlo listo para usar. + programId = compileProgram(vertexShader, fragmentShader); + // std::cout << "programId = " << programId << std::endl; + } + else + { + std::cout << "WARNING: El driver del renderer no es OpenGL." << std::endl; + usingOpenGL = false; + return false; + } + usingOpenGL = true; + return true; + } + + void render() + { + GLint oldProgramId; + // Guarrada para obtener el textureid (en driverdata->texture) + // Detach the texture + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + + SDL_SetRenderTarget(renderer, NULL); + SDL_RenderClear(renderer); + + if (usingOpenGL) + { + SDL_GL_BindTexture(backBuffer, NULL, NULL); + if (programId != 0) + { + glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId); + glUseProgram(programId); + } + + GLfloat minx, miny, maxx, maxy; + GLfloat minu, maxu, minv, maxv; + + // Coordenadas de la ventana donde pintar. + minx = 0.0f; + miny = 0.0f; + maxx = tex_size.x; + maxy = tex_size.y; + + minu = 0.0f; + maxu = 1.0f; + minv = 0.0f; + maxv = 1.0f; + + glViewport(0, 0, win_size.x, win_size.y); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(minu, minv); + glVertex2f(minx, miny); + glTexCoord2f(maxu, minv); + glVertex2f(maxx, miny); + glTexCoord2f(minu, maxv); + glVertex2f(minx, maxy); + glTexCoord2f(maxu, maxv); + glVertex2f(maxx, maxy); + glEnd(); + SDL_GL_SwapWindow(win); + + if (programId != 0) + { + glUseProgram(oldProgramId); + } + } + else + { + SDL_RenderCopy(renderer, backBuffer, NULL, NULL); + SDL_RenderPresent(renderer); + } + } +} +#endif \ No newline at end of file diff --git a/source/jail_engine/jail_shader.h b/source/jail_engine/jail_shader.h new file mode 100644 index 0000000..5c113ad --- /dev/null +++ b/source/jail_engine/jail_shader.h @@ -0,0 +1,48 @@ +#ifndef NO_SHADERS + +#pragma once + +#include // para SDL_Texture +#include // para SDL_Window + +// TIPS: +// ======================================================================= +// Abans de crear el renderer, cridar a la següent funció: +// +// SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); +// +// Aixó li diu que volem un renderer que use especificament opengl. A més, +// al crear el renderer li tenim que dir que el volem que use acceeració +// per hardware, i que soporte render a textura. Per exemple: +// +// SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | +// SDL_RENDERER_TARGETTEXTURE); +// +// Per altra part, al crear la textura tenim que definir que puga ser target +// de renderitzat (SDL_TEXTUREACCESS_TARGET), per exemple: +// +// SDL_Texture *tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, +// SDL_TEXTUREACCESS_TARGET, 320, 240); +// +// Els shaders li'ls passem com una cadena, som nosaltres els que s'encarreguem +// de carregarlos de disc, amb fopen, ifstream, jfile o el que vullgues. +// Si els tens en un std::string, passa-li-la com "cadena.c_str()". +// +// Poden ser els dos el mateix arxiu, com fa libRetro, jo desde dins ja fique +// els defines necessaris. Si es el mateix arxiu, pots no ficar el quart paràmetre. +// +// Els shaders de libRetro no funcionen directament, hi ha que fer algunes modificacions. +// +// El pintat final de la teua escena l'has de fer com si "backBuffer" fora la pantalla. +// +// Ah! una cosa mes: al compilar, en Linux afegir "-lGL", en Windows afegir "-lopengl32". +// En Mac ni idea + +namespace shader +{ + const bool init(SDL_Window *win, SDL_Texture *backBuffer, + const char *vertexShader, const char *fragmentShader = nullptr); + + void render(); +} +#endif \ No newline at end of file diff --git a/source/jail_engine/screen.cpp b/source/jail_engine/screen.cpp index 8ce1b44..d317aef 100644 --- a/source/jail_engine/screen.cpp +++ b/source/jail_engine/screen.cpp @@ -1,5 +1,8 @@ #include "screen.h" +#include "jail_shader.h" #include +#include // Para basic_ifstream, ifstream +#include // Para istreambuf_iterator, operator== #include // Constructor @@ -51,6 +54,9 @@ Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options // Inicializa variables notifyActive = false; + + // Muestra la ventana + SDL_ShowWindow(window); } // Destructor @@ -81,29 +87,19 @@ void Screen::startDrawOnBorder() } // Vuelca el contenido del renderizador en pantalla -void Screen::blit() +void Screen::render() { - // 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 del borde en la ventana - if (options->borderEnabled) - { - SDL_RenderCopy(renderer, borderCanvas, nullptr, nullptr); - } - - // Copia la textura de juego en la ventana en la posición adecuada - SDL_RenderCopy(renderer, gameCanvas, nullptr, &dest); - - // Dibuja las notificaciones + // Renderiza sobre gameCanvas los overlays renderNotifications(); - // Muestra por pantalla el renderizador - SDL_RenderPresent(renderer); + // Si está el borde activo, vuelca gameCanvas sobre borderCanvas + if (options->borderEnabled) + { + gameCanvasToBorderCanvas(); + } + + // Muestra el contenido por pantalla + renderPresent(); } // Establece el modo de video @@ -118,9 +114,6 @@ void Screen::setVideoMode(int videoMode) // Muestra el puntero SDL_ShowCursor(SDL_ENABLE); - // Esconde la ventana - // SDL_HideWindow(window); - // Modifica el tamaño de la ventana en función del borde if (options->borderEnabled) { @@ -139,9 +132,6 @@ void Screen::setVideoMode(int videoMode) // Modifica el tamaño de la ventana SDL_SetWindowSize(window, windowWidth * options->windowSize, windowHeight * options->windowSize); SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); - - // Muestra la ventana - // SDL_ShowWindow(window); } // Si está activo el modo de pantalla completa añade el borde @@ -204,10 +194,27 @@ void Screen::setVideoMode(int videoMode) // Establece el tamaño de las notificaciones setNotificationSize(); + + // Reinicia los shaders + if (options->shaders) + { + const std::string glsl_file = options->screen.windowHeight == 192 ? "crtpi_192.glsl" : "crtpi_240.glsl"; + std::ifstream f(asset->get(glsl_file).c_str()); + std::string source((std::istreambuf_iterator(f)), std::istreambuf_iterator()); + + if (options->borderEnabled) + { + shader::init(window, borderCanvas, source.c_str()); + } + else + { + shader::init(window, gameCanvas, source.c_str()); + } + } } // Camibia entre pantalla completa y ventana -void Screen::switchVideoMode() +void Screen::toggleVideoMode() { options->videoMode = (options->videoMode == 0) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0; setVideoMode(options->videoMode); @@ -240,10 +247,11 @@ void Screen::incWindowSize() void Screen::setBorderColor(color_t color) { borderColor = color; + auto temp = SDL_GetRenderTarget(renderer); SDL_SetRenderTarget(renderer, borderCanvas); SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, 0xFF); SDL_RenderClear(renderer); - SDL_SetRenderTarget(renderer, nullptr); + SDL_SetRenderTarget(renderer, temp); } // Cambia el tipo de mezcla @@ -271,7 +279,7 @@ void Screen::setBorderEnabled(bool value) } // Cambia entre borde visible y no visible -void Screen::switchBorder() +void Screen::toggleBorder() { options->borderEnabled = !options->borderEnabled; setVideoMode(0); @@ -428,14 +436,10 @@ void Screen::showNotification(std::string text1, std::string text2, int icon) // Dibuja las notificaciones void Screen::renderNotifications() { - if (!notifyActive) + if (notifyActive) { - return; + notify->render(); } - - SDL_RenderSetLogicalSize(renderer, notificationLogicalWidth, notificationLogicalHeight); - notify->render(); - SDL_RenderSetLogicalSize(renderer, windowWidth, windowHeight); } // Establece el tamaño de las notificaciones @@ -460,4 +464,48 @@ void Screen::setNotificationSize() notificationLogicalWidth = windowWidth / 3; notificationLogicalHeight = windowHeight / 3; } +} + +// Copia el gameCanvas en el borderCanvas +void Screen::gameCanvasToBorderCanvas() +{ + auto temp = SDL_GetRenderTarget(renderer); + SDL_SetRenderTarget(renderer, borderCanvas); + SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, 0xFF); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, gameCanvas, nullptr, &dest); + SDL_SetRenderTarget(renderer, temp); +} + +// Muestra el contenido de Screen por pantalla +void Screen::renderPresent() +{ + SDL_SetRenderTarget(renderer, nullptr); + SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, 0xFF); + SDL_RenderClear(renderer); + + if (options->shaders) + { + // Aplica shaders y renderiza el contenido + shader::render(); + } + else + { + if (options->borderEnabled) + { + SDL_RenderCopy(renderer, borderCanvas, nullptr, nullptr); + } + else + { + SDL_RenderCopy(renderer, gameCanvas, nullptr, &dest); + } + SDL_RenderPresent(renderer); + } +} + +// Cambia el estado de los shaders +void Screen::toggleShaders() +{ + options->shaders = !options->shaders; + setVideoMode(options->videoMode); } \ No newline at end of file diff --git a/source/jail_engine/screen.h b/source/jail_engine/screen.h index 3f93645..27cfbbd 100644 --- a/source/jail_engine/screen.h +++ b/source/jail_engine/screen.h @@ -69,6 +69,12 @@ private: // Establece el tamaño de las notificaciones void setNotificationSize(); + // Copia el gameCanvas en el borderCanvas + void gameCanvasToBorderCanvas(); + + // Muestra el contenido de Screen por pantalla + void renderPresent(); + public: // Constructor Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options_t *options); @@ -86,13 +92,13 @@ public: void startDrawOnBorder(); // Vuelca el contenido del renderizador en pantalla - void blit(); + void render(); // Establece el modo de video void setVideoMode(int videoMode); // Camibia entre pantalla completa y ventana - void switchVideoMode(); + void toggleVideoMode(); // Cambia el tamaño de la ventana void setWindowSize(int size); @@ -117,7 +123,7 @@ public: void setBorderEnabled(bool value); // Cambia entre borde visible y no visible - void switchBorder(); + void toggleBorder(); // Activa el fade void setFade(); @@ -142,6 +148,9 @@ public: // Muestra una notificación de texto por pantalla; void showNotification(std::string text1 = "", std::string text2 = "", int icon = -1); + + // Cambia el estado de los shaders + void toggleShaders(); }; #endif diff --git a/source/jail_engine/utils.h b/source/jail_engine/utils.h index 60f2136..dc987d9 100644 --- a/source/jail_engine/utils.h +++ b/source/jail_engine/utils.h @@ -132,6 +132,7 @@ struct options_t int windowSize; // Contiene el valor por el que se multiplica el tamaño de la ventana Uint32 filter; // Filtro usado para el escalado de la imagen bool vSync; // Indica si se quiere usar vsync o no + bool shaders; // Indica si se van a usar shaders o no int gameWidth; // Ancho de la resolucion nativa del juego int gameHeight; // Alto de la resolucion nativa del juego bool integerScale; // Indica si el escalado de la imagen ha de ser entero en el modo a pantalla completa