diff --git a/data/gbc.glsl b/data/gbc.glsl new file mode 100644 index 0000000..8dc26f2 --- /dev/null +++ b/data/gbc.glsl @@ -0,0 +1,38 @@ +varying vec2 tex_coord; +varying vec2 pix_coord; + +#if defined(VERTEX) + +void main() +{ + pix_coord = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001; + tex_coord = vec2((gl_Vertex.x+1.0)*0.5, (-gl_Vertex.y+1.0)*0.5); + vec4 pos = vec4(gl_Vertex.x * 2.0, gl_Vertex.y * 2.0, gl_Vertex.z, gl_Vertex.w); + gl_Position = gl_Vertex; //(gl_Vertex*2)-vec3(1.0, 1.0, 1.0);//gl_ModelViewProjectionMatrix * gl_Vertex; +} + +#elif defined(FRAGMENT) + +uniform sampler2D Texture; + +void main() +{ + float x = sign(pix_coord.x)*floor(abs(pix_coord.x)+0.5); + float y = sign(pix_coord.y)*floor(abs(pix_coord.y)+0.5); + float column = mod(x,4.0); + float row = mod(y,4.0); + vec4 color = texture2D(Texture, tex_coord); + vec4 newcolor; + if ((column == 0.0) || (row == 0.0) ) { + newcolor = color * vec4(0.4, 0.4, 0.4, 1.0); + } else if ((column == 1.0) || (row == 1.0) ) { + newcolor = color * vec4(0.6, 0.7, 0.8, 1.0); + } else if ((column == 3.0) || (row == 3.0) ) { + newcolor = color * vec4(0.8, 0.7, 0.6, 1.0); + } else { + newcolor = color; + } + gl_FragColor = newcolor; +} + +#endif \ No newline at end of file diff --git a/data/lynx.glsl b/data/lynx.glsl new file mode 100644 index 0000000..6b6787f --- /dev/null +++ b/data/lynx.glsl @@ -0,0 +1,41 @@ +varying vec2 tex_coord; +varying vec2 pix_coord; + +#if defined(VERTEX) + +void main() +{ + pix_coord = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001; + tex_coord = vec2((gl_Vertex.x+1.0)*0.5, (-gl_Vertex.y+1.0)*0.5); + vec4 pos = vec4(gl_Vertex.x * 2.0, gl_Vertex.y * 2.0, gl_Vertex.z, gl_Vertex.w); + gl_Position = gl_Vertex; //(gl_Vertex*2)-vec3(1.0, 1.0, 1.0);//gl_ModelViewProjectionMatrix * gl_Vertex; +} + +#elif defined(FRAGMENT) + +uniform sampler2D Texture; + +void main() +{ + float x = sign(pix_coord.x)*floor(abs(pix_coord.x)+0.5); + float column = mod(x,4.0); + vec4 color = texture2D(Texture, tex_coord); + float xfade = abs((tex_coord.s * 2.0) - 1.0); + xfade = xfade * xfade * xfade * xfade * xfade; + float yfade = abs((tex_coord.t * 2.0) - 1.0); + yfade = yfade * yfade * yfade * yfade * yfade; + color = color + vec4(0.7, 0.7, 0.7, 0.0) * (1.0-tex_coord.t) * (1.0-xfade) * (1.0-yfade); + vec4 newcolor; + if (column == 0.0) { + newcolor = color * vec4(1.0, 0.4, 0.6, 1.0); + } else if (column == 1.0) { + newcolor = color * vec4(0.4, 1.0, 0.4, 1.0); + } else if (column == 2.0) { + newcolor = color * vec4(0.6, 0.4, 1.0, 1.0); + } else { + newcolor = color * vec4(0.2, 0.2, 0.2, 1.0); + } + gl_FragColor = newcolor; +} + +#endif diff --git a/source/aux_textfile.cpp b/source/aux_textfile.cpp index f7fbba0..69385b6 100644 --- a/source/aux_textfile.cpp +++ b/source/aux_textfile.cpp @@ -22,7 +22,7 @@ namespace textfile const bool open(std::string filename) { - buffer = file_getfilebuffer(filename.c_str(), fsize); + buffer = file::getFileBuffer(filename.c_str(), fsize); p = 0; return true; diff --git a/source/gamestate_password.cpp b/source/gamestate_password.cpp index e7453e7..9fca910 100644 --- a/source/gamestate_password.cpp +++ b/source/gamestate_password.cpp @@ -120,7 +120,7 @@ namespace gamestate { password[10] = 0; int filesize = 0; - const char *buffer = file_getfilebuffer("offsets.bal", filesize); + const char *buffer = file::getFileBuffer("offsets.bal", filesize); int punter = 0; diff --git a/source/gamestate_postfase.cpp b/source/gamestate_postfase.cpp index 4fa8cbb..74e4e61 100644 --- a/source/gamestate_postfase.cpp +++ b/source/gamestate_postfase.cpp @@ -104,7 +104,7 @@ namespace gamestate std::string getPassword() { int size; - char *buffer = file_getfilebuffer("offsets.bal", size); + char *buffer = file::getFileBuffer("offsets.bal", size); int punter = (game::getConfig("fase")-1)*10; char passFile[11]; diff --git a/source/gamestate_sequence.cpp b/source/gamestate_sequence.cpp index 40783b7..ef4416a 100644 --- a/source/gamestate_sequence.cpp +++ b/source/gamestate_sequence.cpp @@ -34,7 +34,7 @@ namespace gamestate } int size; - sequence_file = file_getfilepointer(filename.c_str(), size); + sequence_file = file::getFilePointer(filename.c_str(), size); game::setState(&gamestate::sequence::loop); } diff --git a/source/jaudio.cpp b/source/jaudio.cpp index 1815095..ced852b 100644 --- a/source/jaudio.cpp +++ b/source/jaudio.cpp @@ -155,7 +155,7 @@ namespace audio { audio::music *loadMusic(const char* filename) { int size; - char *buffer = file_getfilebuffer(filename, size); + char *buffer = file::getFileBuffer(filename, size); if (buffer == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::loadMusic): No s'ha trobat l'arxiu %s\n", filename); exit(1); @@ -322,7 +322,7 @@ namespace audio { audio::sound *loadSound(const char* filename) { int size; - uint8_t *buffer = (uint8_t *)file_getfilebuffer(filename, size); + uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size); if (buffer == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::loadSound): No s'ha trobat l'arxiu %s\n", filename); diff --git a/source/jdraw.cpp b/source/jdraw.cpp index a45d508..e6d741c 100644 --- a/source/jdraw.cpp +++ b/source/jdraw.cpp @@ -2,6 +2,7 @@ #include "SDL3/SDL.h" #include "gif.h" #include "jfile.h" +#include "jshader.h" namespace draw { @@ -13,9 +14,13 @@ namespace draw SDL_Window *sdl_window {nullptr}; // La finestra de SDL SDL_Renderer *sdl_renderer {nullptr}; // El renderer de SDL SDL_Texture *sdl_texture {nullptr}; // La textura de SDL a la que pintarem la nostra superficie "screen" i que despres volcarem a pantalla + SDL_Texture *sdl_shadertex {nullptr}; // La textura de SDL per al shader static int screen_zoom = 1; static bool screen_fullscreen = false; + static bool screen_cursor = true; + static char* screen_shader = nullptr; + static bool shader_enabled = false; static float window_ratio = 1; static int canvas_width; static int canvas_height; @@ -51,13 +56,17 @@ namespace draw // Ajustem el tamany de la finestra, segons el zoom i el ratio window_width = canvas_width*screen_zoom; - window_height = window_ratio != 1 ? canvas_width*window_ratio*screen_zoom : canvas_height*screen_zoom; + window_height = window_ratio != 1 ? int(float(canvas_width)*window_ratio*float(screen_zoom)) : canvas_height*screen_zoom; // Mentres no càpiga en la pantalla, reduïm el zoom while (window_width > desktop_width || window_height > desktop_height) { screen_zoom--; window_width = canvas_width*screen_zoom; - window_height = window_ratio != 1 ? canvas_width*window_ratio*screen_zoom : canvas_height*screen_zoom; + window_height = window_ratio != 1 ? int(float(canvas_width)*window_ratio*float(screen_zoom)) : canvas_height*screen_zoom; + } + if (screen_fullscreen) { + window_width = desktop_width; + window_height = desktop_height; } sdl_window = SDL_CreateWindow(window_title, window_width, window_height, SDL_WINDOW_OPENGL|(screen_fullscreen?SDL_WINDOW_FULLSCREEN:0)); @@ -66,6 +75,25 @@ namespace draw exit(1); } + sdl_renderer = SDL_CreateRenderer(sdl_window, NULL); + if (!sdl_renderer) { + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize renderer!\n"); + exit(1); + } + + if (screen_cursor) SDL_ShowCursor(); else SDL_HideCursor(); + + sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, canvas_width, canvas_height); + SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_NEAREST); + if (!sdl_texture) { + SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize texture!\n"); + exit(1); + } + + sdl_shadertex = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, window_width, window_height); + SDL_SetTextureScaleMode(sdl_shadertex, SDL_SCALEMODE_NEAREST); + + loadShader(); } void destroyDisplay() @@ -93,7 +121,9 @@ namespace draw desktop_width = dm->w; desktop_height = dm->h; + createDisplay(); // Inicialització de les estructures de SDL + /* sdl_window = SDL_CreateWindow(titol, width * zoom, height * zoom, 0); if (!sdl_window) { SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize window!\n"); @@ -110,6 +140,7 @@ namespace draw SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize texture!\n"); exit(1); } + */ // Creem la superficie "screen" i la establim com a superficie destinació screen = createSurface(width, height); @@ -120,7 +151,7 @@ namespace draw sel_color = transparent = 0; for (int i=0;i<256;++i) color_indices[i] = i; - SDL_HideCursor(); + //SDL_HideCursor(); //textsurf = loadSurface("font.gif"); } @@ -136,9 +167,7 @@ namespace draw } // Destruim tot el relacionat amb SDL - SDL_DestroyTexture(sdl_texture); - SDL_DestroyRenderer(sdl_renderer); - SDL_DestroyWindow(sdl_window); + destroyDisplay(); // Fiquem tots els punters a nullptr, per si de cas no estem eixint del programa // i anem a tornar a inicialitzar el sistema @@ -148,11 +177,81 @@ namespace draw screen = destination = source = nullptr; } + void setZoom(const int value) { + screen_zoom = value; + destroyDisplay(); + createDisplay(); + char strzoom[3]; + file::setConfigValue("zoom", SDL_itoa(screen_zoom, strzoom, 10)); + } + const int getZoom() { return screen_zoom; } + const float getScaleX() + { + return float(window_width) / float(canvas_width); + } + + const float getScaleY() + { + return float(window_height) / float(canvas_height); + } + + bool getFullscreen() { + return screen_fullscreen; + } + + void setFullscreen(const bool value) { + screen_fullscreen=value; + destroyDisplay(); + createDisplay(); + file::setConfigValue("fullscreen", screen_fullscreen?"true":"false"); + } + + void loadShader() + { + char *buffer = nullptr; + if (screen_shader) { + int size; + buffer = file::getFileBuffer(screen_shader, size, true); + } + shader::setAspectRatio(3.0f/4.0f); + shader::init(sdl_window, sdl_shadertex, buffer); + if (buffer) free(buffer); + } + + void setShader(const char* shader_file) + { + if (screen_shader) free(screen_shader); + screen_shader = (char*)malloc(strlen(shader_file)+1); + strcpy(screen_shader, shader_file); + loadShader(); + } + + void enableShader() + { + shader_enabled = true; + shader::enable(); + //destroyDisplay(); + //createDisplay(); + } + + void disableShader() + { + shader_enabled = false; + shader::disable(); + //destroyDisplay(); + //createDisplay(); + } + + void toggleShader() + { + shader_enabled ? disableShader() : enableShader(); + } + // Crea una superficie i torna un punter a ella surface *createSurface(const uint16_t w, const uint16_t h) { @@ -176,7 +275,7 @@ namespace draw // Agafem un buffer de bytes de l'arxiu especificat // getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char int size; - uint8_t *buffer = (uint8_t *)file_getfilebuffer(filename, size); + uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size); // Si ens ha tornat nullptr, es que no l'ha trobat, tornem nosaltres també nullptr ja que no s'ha pogut crear la superficie if (buffer == nullptr) @@ -292,7 +391,7 @@ namespace draw // Agafem un buffer de bytes de l'arxiu especificat // getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char int size; - uint8_t *buffer = (uint8_t *)file_getfilebuffer(filename, size); + uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size); // Li passem el array del arxiu a LoadPalette. Ell ens torna un array de uint32_t amb la paleta // Van a ser 256 entrades de 32 bits, cada entrada es un color, amb el format 0xAARRGGBB @@ -565,11 +664,13 @@ namespace draw // Desbloquejem la textura SDL_UnlockTexture(sdl_texture); + SDL_SetRenderTarget(sdl_renderer, sdl_shadertex); // Pintem la textura a pantalla SDL_RenderTexture(sdl_renderer, sdl_texture, NULL, NULL); // I ho presentem - SDL_RenderPresent(sdl_renderer); + shader::render(); + //SDL_RenderPresent(sdl_renderer); } } diff --git a/source/jdraw.h b/source/jdraw.h index 126907b..04f5d40 100644 --- a/source/jdraw.h +++ b/source/jdraw.h @@ -25,13 +25,25 @@ namespace draw /// @param width es el ample de la finestra "virtual" /// @param height es el alt de la finestra "virtual" /// @param zoom es com de grans son els pixels. - void init(const char *titol, const uint16_t width, const uint16_t height, const int zoom, const float ratio=1.0); + void init(const char *titol, const uint16_t width, const uint16_t height, const int zoom, const bool fullscreen=false, const float ratio=1.0); /// @brief Finalització del sistema (tancar coses de SDL, superficies fixes, etc...) void quit(); + void setZoom(const int value); const int getZoom(); - + const float getScaleX(); + const float getScaleY(); + + bool getFullscreen(); + void setFullscreen(const bool value); + + void loadShader(); + void setShader(const char* shader_file); + void enableShader(); + void disableShader(); + void toggleShader(); + /// @brief Crea una superficie i torna un punter a ella /// @param w ample de la superficie /// @param h alt de la superficie diff --git a/source/jfile.cpp b/source/jfile.cpp index daa38ba..03eea08 100644 --- a/source/jfile.cpp +++ b/source/jfile.cpp @@ -19,213 +19,216 @@ #define DEFAULT_FOLDER "data/" #define CONFIG_FILENAME "config.txt" -struct file_t +namespace file { - std::string path; - uint32_t size; - uint32_t offset; -}; - -std::vector toc; - -/* El std::map me fa coses rares, vaig a usar un good old std::vector amb una estructura key,value propia i au, que sempre funciona */ -struct keyvalue_t { - std::string key, value; -}; - -char *resource_filename = NULL; -char *resource_folder = NULL; -int file_source = SOURCE_FILE; -char scratch[255]; -static std::string config_folder; -std::vector config; - -void file_setresourcefilename(const char *str) { - if (resource_filename != NULL) free(resource_filename); - resource_filename = (char*)malloc(strlen(str)+1); - strcpy(resource_filename, str); -} - -void file_setresourcefolder(const char *str) { - if (resource_folder != NULL) free(resource_folder); - resource_folder = (char*)malloc(strlen(str)+1); - strcpy(resource_folder, str); -} - -void file_setsource(const int src) { - file_source = src%2; // mod 2 so it always is a valid value, 0 (file) or 1 (folder) - if (src==SOURCE_FOLDER && resource_folder==NULL) file_setresourcefolder(DEFAULT_FOLDER); -} - -bool file_getdictionary() { - if (resource_filename == NULL) file_setresourcefilename(DEFAULT_FILENAME); - - std::ifstream fi (resource_filename, std::ios::binary); - if (!fi.is_open()) return false; - char header[4]; - fi.read(header, 4); - uint32_t num_files, toc_offset; - fi.read((char*)&num_files, 4); - fi.read((char*)&toc_offset, 4); - fi.seekg(toc_offset); - - for (uint i=0; ipw_dir; - config_folder = std::string(homedir) + "/Library/Application Support/" + foldername; -#elif __linux__ - struct passwd *pw = getpwuid(getuid()); - const char *homedir = pw->pw_dir; - config_folder = std::string(homedir) + "/." + foldername; -#endif - - struct stat st = {0}; - if (stat(config_folder.c_str(), &st) == -1) + struct file_t { -#ifdef _WIN32 - int ret = mkdir(config_folder.c_str()); -#else - int ret = mkdir(config_folder.c_str(), S_IRWXU); -#endif + std::string path; + uint32_t size; + uint32_t offset; + }; - if (ret == -1) + std::vector toc; + + /* El std::map me fa coses rares, vaig a usar un good old std::vector amb una estructura key,value propia i au, que sempre funciona */ + struct keyvalue_t { + std::string key, value; + }; + + char *resource_filename = NULL; + char *resource_folder = NULL; + int file_source = SOURCE_FILE; + char scratch[255]; + static std::string config_folder; + std::vector config; + + void setResourceFilename(const char *str) { + if (resource_filename != NULL) free(resource_filename); + resource_filename = (char*)malloc(strlen(str)+1); + strcpy(resource_filename, str); + } + + void setResourceFolder(const char *str) { + if (resource_folder != NULL) free(resource_folder); + resource_folder = (char*)malloc(strlen(str)+1); + strcpy(resource_folder, str); + } + + void setSource(const int src) { + file_source = src%2; // mod 2 so it always is a valid value, 0 (file) or 1 (folder) + if (src==SOURCE_FOLDER && resource_folder==NULL) setResourceFolder(DEFAULT_FOLDER); + } + + bool getDictionary() { + if (resource_filename == NULL) setResourceFilename(DEFAULT_FILENAME); + + std::ifstream fi (resource_filename, std::ios::binary); + if (!fi.is_open()) return false; + char header[4]; + fi.read(header, 4); + uint32_t num_files, toc_offset; + fi.read((char*)&num_files, 4); + fi.read((char*)&toc_offset, 4); + fi.seekg(toc_offset); + + for (uint i=0; ipw_dir; + config_folder = std::string(homedir) + "/Library/Application Support/" + foldername; + #elif __linux__ + struct passwd *pw = getpwuid(getuid()); + const char *homedir = pw->pw_dir; + config_folder = std::string(homedir) + "/." + foldername; + #endif + + struct stat st = {0}; + if (stat(config_folder.c_str(), &st) == -1) + { + #ifdef _WIN32 + int ret = mkdir(config_folder.c_str()); + #else + int ret = mkdir(config_folder.c_str(), S_IRWXU); + #endif + + if (ret == -1) + { + printf("ERROR CREATING CONFIG FOLDER."); + exit(EXIT_FAILURE); + } } } -} -const char *file_getconfigfolder() { - std::string folder = config_folder + "/"; - return folder.c_str(); -} + const char *getConfigFolder() { + std::string folder = config_folder + "/"; + return folder.c_str(); + } -void file_loadconfigvalues() { - config.clear(); - std::string config_file = config_folder + "/config.txt"; - FILE *f = fopen(config_file.c_str(), "r"); - if (!f) return; + void loadConfigValues() { + config.clear(); + std::string config_file = config_folder + "/config.txt"; + FILE *f = fopen(config_file.c_str(), "r"); + if (!f) return; - char line[1024]; - while (fgets(line, sizeof(line), f)) { - char *value = strchr(line, '='); - if (value) { - *value='\0'; value++; - value[strlen(value)-1] = '\0'; - config.push_back({line, value}); - } - } - fclose(f); -} - -void file_saveconfigvalues() { - std::string config_file = config_folder + "/config.txt"; - FILE *f = fopen(config_file.c_str(), "w"); - if (f) { - for (auto pair : config) { - fprintf(f, "%s=%s\n", pair.key.c_str(), pair.value.c_str()); + char line[1024]; + while (fgets(line, sizeof(line), f)) { + char *value = strchr(line, '='); + if (value) { + *value='\0'; value++; + value[strlen(value)-1] = '\0'; + config.push_back({line, value}); + } } fclose(f); } -} -const char* file_getconfigvalue(const char *key) { - if (config.empty()) file_loadconfigvalues(); - for (auto pair : config) { - if (pair.key == std::string(key)) { - strcpy(scratch, pair.value.c_str()); - return scratch; + void saveConfigValues() { + std::string config_file = config_folder + "/config.txt"; + FILE *f = fopen(config_file.c_str(), "w"); + if (f) { + for (auto pair : config) { + fprintf(f, "%s=%s\n", pair.key.c_str(), pair.value.c_str()); + } + fclose(f); } } - return NULL; -} -void file_setconfigvalue(const char* key, const char* value) { - if (config.empty()) file_loadconfigvalues(); - for (auto &pair : config) { - if (pair.key == std::string(key)) { - pair.value = value; - file_saveconfigvalues(); - return; + const char* getConfigValue(const char *key) { + if (config.empty()) loadConfigValues(); + for (auto pair : config) { + if (pair.key == std::string(key)) { + strcpy(scratch, pair.value.c_str()); + return scratch; + } } + return NULL; + } + + void setConfigValue(const char* key, const char* value) { + if (config.empty()) loadConfigValues(); + for (auto &pair : config) { + if (pair.key == std::string(key)) { + pair.value = value; + saveConfigValues(); + return; + } + } + config.push_back({key, value}); + saveConfigValues(); + return; } - config.push_back({key, value}); - file_saveconfigvalues(); - return; } diff --git a/source/jfile.h b/source/jfile.h index 9fe6c9b..9ee4bce 100644 --- a/source/jfile.h +++ b/source/jfile.h @@ -4,15 +4,18 @@ #define SOURCE_FILE 0 #define SOURCE_FOLDER 1 -void file_setconfigfolder(const char *foldername); -const char *file_getconfigfolder(); +namespace file +{ + void setConfigFolder(const char *foldername); + const char *getConfigFolder(); -void file_setresourcefilename(const char *str); -void file_setresourcefolder(const char *str); -void file_setsource(const int src); + void setResourceFilename(const char *str); + void setResourceFolder(const char *str); + void setSource(const int src); -FILE *file_getfilepointer(const char *resourcename, int& filesize, const bool binary=false); -char *file_getfilebuffer(const char *resourcename, int& filesize, const bool zero_terminate=false); + FILE *getFilePointer(const char *resourcename, int& filesize, const bool binary=false); + char *getFileBuffer(const char *resourcename, int& filesize, const bool zero_terminate=false); -const char* file_getconfigvalue(const char *key); -void file_setconfigvalue(const char* key, const char* value); + const char* getConfigValue(const char *key); + void setConfigValue(const char* key, const char* value); +} diff --git a/source/jgame.cpp b/source/jgame.cpp index afe4ec8..679e940 100644 --- a/source/jgame.cpp +++ b/source/jgame.cpp @@ -63,8 +63,8 @@ int main(int argc, char *argv[]) game::windowHasFocus = true; game::init(); - input::init(draw::getZoom()); - + input::init(); + static unsigned int current_ticks = SDL_GetTicks(); bool should_exit=false; @@ -84,7 +84,22 @@ int main(int argc, char *argv[]) } if (e.type==SDL_EVENT_KEY_UP) { - input::updateKeypressed(e.key.scancode); + switch (e.key.scancode) { + case SDL_SCANCODE_F1: + draw::setZoom(draw::getZoom()-1); + break; + case SDL_SCANCODE_F2: + draw::setZoom(draw::getZoom()+1); + break; + case SDL_SCANCODE_F3: + draw::setFullscreen(!draw::getFullscreen()); + break; + case SDL_SCANCODE_F4: + draw::toggleShader(); + break; + default: + input::updateKeypressed(e.key.scancode); + } } if (e.type==SDL_EVENT_MOUSE_BUTTON_UP) { diff --git a/source/jinput.cpp b/source/jinput.cpp index 304f363..36b9f73 100644 --- a/source/jinput.cpp +++ b/source/jinput.cpp @@ -9,12 +9,10 @@ namespace input static uint8_t keydown = 0; static uint8_t btnClicked = 0; static int wheel = 0; - static int screen_zoom = 1; - void init(const int zoom) + void init() { keys = SDL_GetKeyboardState(NULL); - screen_zoom = zoom; } // Determina si la tecla especificada està sent polsada ara mateix @@ -81,7 +79,7 @@ namespace input { float x; SDL_GetMouseState(&x, NULL); - return x/screen_zoom; + return x/draw::getScaleX(); } // Torna la posició Y actual del ratolí @@ -89,7 +87,7 @@ namespace input { float y; SDL_GetMouseState(NULL, &y); - return y/screen_zoom; + return y/draw::getScaleY(); } // Determina si el botó del ratolí especificat està sent polsada ara mateix diff --git a/source/jinput.h b/source/jinput.h index 77cf9f1..8fc7241 100644 --- a/source/jinput.h +++ b/source/jinput.h @@ -14,7 +14,7 @@ namespace input } /// @brief Inicialitza els sistemes de teclat, ratolí i gamepad - void init(const int zoom); + void init(); /// @brief Determina si la tecla especificada està sent polsada ara mateix /// @param key tecla a consultar diff --git a/source/jshader.cpp b/source/jshader.cpp new file mode 100644 index 0000000..bc42c1f --- /dev/null +++ b/source/jshader.cpp @@ -0,0 +1,270 @@ +#include "jshader.h" + +#include + +#ifdef __APPLE__ +#include "CoreFoundation/CoreFoundation.h" +#include + +#if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 +#include +#else +#include +#endif //!ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 +#else +#include +#include +#endif + +namespace shader +{ + SDL_Window *win = nullptr; + SDL_Renderer *renderer = nullptr; + GLuint programId = 0; + SDL_Texture* backBuffer = nullptr; + SDL_FRect window = {0, 0, 640, 480}; + SDL_FPoint tex_size = {320, 240}; + float aspect_ratio = 1; + bool can_use_opengl = false; + bool using_opengl = false; + GLuint texture_number; + GLuint nose; + + #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; + PFNGLDELETEPROGRAMPROC glDeleteProgram; + PFNGLLINKPROGRAMPROC glLinkProgram; + PFNGLVALIDATEPROGRAMPROC glValidateProgram; + PFNGLGETPROGRAMIVPROC glGetProgramiv; + PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; + PFNGLUSEPROGRAMPROC glUseProgram; + PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; + + 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"); + glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram"); + 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"); + glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)SDL_GL_GetProcAddress("glGetUniformLocation"); + + return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv && + glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram && + glDeleteProgram && glLinkProgram && glValidateProgram && glGetProgramiv && + glGetProgramInfoLog && glUseProgram && glGetUniformLocation; + } + + #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; + //std::cout << source << std::endl; + free(log); + } + glDeleteShader(result); + result = 0; + } + return result; + } + + GLuint compileProgram(const char* vertexShaderSource, const char* fragmentShaderSource) + { + GLuint programId = 0; + GLuint vtxShaderId, fragShaderId; + + if (programId != 0) glDeleteProgram(programId); + 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; + int w, h; + SDL_GetWindowSize(win, &w, &h); + + if (w * aspect_ratio > h) { + window.y = 0; + window.h = h; + window.w = h/aspect_ratio; + window.x = (w - window.w)/2; + } else { + window.x = 0; + window.w = w; + window.h = w*aspect_ratio; + window.y = (h - window.h)/2; + } + + SDL_GetTextureSize(backBuffer, &tex_size.x, &tex_size.y); + printf("tex size: %fx%f\n", tex_size.x, tex_size.y); + SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer); + texture_number = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER, -1); + printf("texture number: %i\n", texture_number); + int access = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_ACCESS_NUMBER, -1); + nose = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_TARGET_NUMBER, -1); + printf("texture target number: %i\n", nose); + + 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); + } + + const char * renderer_name = SDL_GetRendererName(renderer); + printf("rendererInfo.name: %s\n", renderer_name); + + if(!strncmp(renderer_name, "opengl", 6)) { +#ifndef __APPLE__ + static bool gl_extensions_initialized = false; + if (!gl_extensions_initialized) { + if (!initGLExtensions()) { + std::cout << "WARNING: No s'han pogut inicialitzar les extensions d'OpenGL!" << std::endl; + can_use_opengl = false; + return false; + } + gl_extensions_initialized = true; + } +#endif + // Compilar el shader y dejarlo listo para usar. + if (!vertexShader) { + can_use_opengl = false; + return false; + } + programId = compileProgram(vertexShader, fragmentShader); + } else { + std::cout << "WARNING: El driver del renderer no es OpenGL." << std::endl; + can_use_opengl = false; + return false; + } + can_use_opengl = true; + return true; + } + + unsigned char pixels[512*240*4]; + + void enable() { if (can_use_opengl) using_opengl = true; } + void disable() { using_opengl = false; } + + void setAspectRatio(const float ratio) + { + aspect_ratio = ratio; + } + + void render() + { + SDL_FlushRenderer(renderer); + SDL_SetRenderTarget(renderer, NULL); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_FlushRenderer(renderer); + + if (using_opengl) + { + GLint oldProgramId; + if (programId != 0) + { + glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId); + glUseProgram(programId); + } + + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 1); + //glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, pixels); + //if (glGetError()) { printf("GLGETERROR!\n"); exit(1);} + //GLint param; + //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, ¶m); + //printf("tex width: %i\n", param); + glViewport(window.x, window.y, window.w, window.h); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(-1.0f, -1.0f); + glTexCoord2f(tex_size.x, 0.0f); + glVertex2f(1.0f, -1.0f); + glTexCoord2f(0.0f, tex_size.y); + glVertex2f(-1.0f, 1.0f); + glTexCoord2f(tex_size.x, tex_size.y); + glVertex2f(1.0f, 1.0f); + glEnd(); + + SDL_GL_SwapWindow(win); + + if (programId != 0) glUseProgram(oldProgramId); + + } else { + SDL_RenderTexture(renderer, backBuffer, NULL, &window); + SDL_RenderPresent(renderer); + } + if (glGetError()) { printf("GLERROR!\n"); exit(1); } + } +} diff --git a/source/jshader.h b/source/jshader.h new file mode 100644 index 0000000..7f19608 --- /dev/null +++ b/source/jshader.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +// 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 setAspectRatio(const float ratio); + void enable(); + void disable(); + + void render(); +} diff --git a/source/main.cpp b/source/main.cpp index 0df75bf..57c1d99 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -4,12 +4,12 @@ void game::init() { - draw::init("Arounders", 320, 200, 3); + draw::init("Arounders", 320, 200, 3, false, 3.0f/4.0f); + draw::setShader("lynx.glsl"); draw::setTrans(0); - - input::init(3); audio::init(48000, SDL_AUDIO_S16, 2); - + + file::setConfigFolder("arounders"); font::init(); game::setUpdateTicks(16);