From 6208cc814fff23bf93cee00e62da941084fdf48e Mon Sep 17 00:00:00 2001 From: Raimon Zamora Date: Fri, 28 Jun 2024 10:08:48 +0200 Subject: [PATCH] =?UTF-8?q?-=20Tot=20separadet=20en=20una=20m=C3=B2dul.=20?= =?UTF-8?q?Vore=20algo=20de=20instruccions=20en=20jshader.h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 73 +++++++++ Makefile | 4 +- jshader.cpp | 229 +++++++++++++++++++++++++++ jshader.h | 44 +++++ main.cpp | 361 +++--------------------------------------- pang.bmp | Bin 0 -> 276534 bytes 6 files changed, 373 insertions(+), 338 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 jshader.cpp create mode 100644 jshader.h create mode 100644 pang.bmp diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e47cdf8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,73 @@ +{ + "files.associations": { + "string": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "hash_map": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "cstdint": "cpp", + "deque": "cpp", + "list": "cpp", + "map": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "format": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "semaphore": "cpp", + "span": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stdfloat": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "text_encoding": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "variant": "cpp" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index 098c37b..3f9c45e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ windows: - g++ main.cpp -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -lopengl32 -mwindows -o shaders + g++ *.cpp -lmingw32 -lSDL2main -lSDL2 -lopengl32 -mwindows -o shaders linux: - g++ main.cpp -lSDL2 -lSDL2_image -lGL -o shaders + g++ -g *.cpp -lSDL2 -lGL -o shaders diff --git a/jshader.cpp b/jshader.cpp new file mode 100644 index 0000000..06ea620 --- /dev/null +++ b/jshader.cpp @@ -0,0 +1,229 @@ +#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_Point win_size = {640, 480}; + SDL_Point tex_size = {320, 240}; + 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); + } + } +} diff --git a/jshader.h b/jshader.h new file mode 100644 index 0000000..556f796 --- /dev/null +++ b/jshader.h @@ -0,0 +1,44 @@ +#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 render(); +} diff --git a/main.cpp b/main.cpp index 52281c5..1ac89c2 100644 --- a/main.cpp +++ b/main.cpp @@ -1,355 +1,44 @@ #include - #include #include #include - +#include "jshader.h" #include -#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 - -const int WIN_WIDTH = 1024; -const int WIN_HEIGHT = 768; - -const int WORLD_WIDTH = 320; -const int WORLD_HEIGHT = 240; - -struct sprite { - float x; - float y; - float vx; - float vy; -}; - -const int MAX_SPRITES = 20; - -#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) { - //std::cout << "Compilando shader:" << std::endl << source << std::endl; - // Create ID for shader - GLuint result = glCreateShader(shaderType); - - 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* vtxFile, const char* fragFile) { - GLuint programId = 0; - GLuint vtxShaderId, fragShaderId; - - programId = glCreateProgram(); - - std::ifstream f(vtxFile); - std::string source((std::istreambuf_iterator(f)), - std::istreambuf_iterator()); - - vtxShaderId = compileShader(source.c_str(), GL_VERTEX_SHADER); - - f=std::ifstream(fragFile); - source=std::string((std::istreambuf_iterator(f)), - std::istreambuf_iterator()); - fragShaderId = compileShader(source.c_str(), 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; -} - -void presentBackBuffer(SDL_Renderer *renderer, SDL_Window* win, SDL_Texture* backBuffer, GLuint programId) { - 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); - - 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 = WORLD_WIDTH; - maxy = WORLD_HEIGHT; - - minu = 0.0f; - maxu = 1.0f; - minv = 0.0f; - maxv = 1.0f; - - glViewport(0, 0, WIN_WIDTH, WIN_HEIGHT); - - 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); - } -} - -#ifdef __APPLE__ -void initializeFileSystem() { - CFBundleRef mainBundle = CFBundleGetMainBundle(); - CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); - char path[PATH_MAX]; - if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX)) - { - std::cerr << "Couldn't get file system representation! " << std::endl; - } - CFRelease(resourcesURL); - chdir(path); -} -#endif - -int main(int argc, char **argv){ - - if (argc < 2) { - std::cerr << "Uso: shaders [vertex shader] [fragment shader]" << "\n"; - std::cerr << " shaders [unique shader]" << "\n"; - return 1; - } - char *vertex_shader = argv[1]; - char *fragment_shader = argc<3 ? vertex_shader : argv[2]; - - GLuint programId; - -#ifdef __APPLE__ - initializeFileSystem(); -#endif - - if (SDL_Init(SDL_INIT_EVERYTHING | SDL_VIDEO_OPENGL) != 0){ - std::cerr << "SDL_Init failed: " << SDL_GetError() << "\n"; - return 1; - } - - SDL_Window *win = SDL_CreateWindow("Custom shader with SDL2 renderer!", SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, WIN_WIDTH, WIN_HEIGHT, 0); +#define WIDTH 384 +#define HEIGHT 240 +int main(int argc, char **argv) +{ + SDL_Init(SDL_INIT_EVERYTHING); + SDL_Window *win = SDL_CreateWindow("Shaders i tal", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIDTH*3, HEIGHT*3, 0); SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); + SDL_Renderer *renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); + SDL_Texture *texTarget = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, WIDTH, HEIGHT); - SDL_Renderer *renderer = SDL_CreateRenderer(win, -1, - SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); + std::ifstream f("crtpi.glsl"); + std::string source((std::istreambuf_iterator(f)), std::istreambuf_iterator()); - SDL_RendererInfo rendererInfo; - SDL_GetRendererInfo(renderer, &rendererInfo); + shader::init(win, texTarget, source.c_str()); - if(!strncmp(rendererInfo.name, "opengl", 6)) { - std::cout << "Es OpenGL!" << std::endl; -#ifndef __APPLE__ - // If you want to use GLEW or some other GL extension handler, do it here! - if (!initGLExtensions()) { - std::cout << "Couldn't init GL extensions!" << std::endl; - SDL_Quit(); - exit(-1); + // Carreguem una imatge pa ficar algo + SDL_Texture *bmpTex = SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("pang.bmp")); + + bool should_exit = false; + + while (!should_exit) + { + SDL_Event e; + while ( SDL_PollEvent(&e) ) { + if ( e.type == SDL_QUIT || ( e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE ) ) { should_exit = true; } } -#endif - // Compilar el shader y dejarlo listo para usar. - programId = compileProgram(vertex_shader, fragment_shader); - std::cout << "programId = " << programId << std::endl; - } - //Put your own bmp image here - SDL_Surface *bmpSurf = IMG_Load("image.png"); - SDL_Texture *bmpTex = SDL_CreateTextureFromSurface(renderer, bmpSurf); - SDL_FreeSurface(bmpSurf); - - //Make a target texture to render too - SDL_Texture *texTarget = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, - SDL_TEXTUREACCESS_TARGET, WORLD_WIDTH, WORLD_HEIGHT); - - int done = 0; - int useShader = 0; - SDL_Rect targetRect; - - targetRect.x = 0; targetRect.y=0; targetRect.w=32; targetRect.h=32; - - struct sprite sprites[MAX_SPRITES]; - - for(int i=0;i < MAX_SPRITES;++i) { - sprites[i].x = (float) (rand() % (WORLD_WIDTH - targetRect.w)); - sprites[i].y = (float) (rand() % (WORLD_HEIGHT - targetRect.h)); - sprites[i].vx = (float) (rand() % 5 / 10.0f) - 0.2f; - sprites[i].vy = (float) (rand() % 5 / 10.0f) - 0.2f; - } - - // Voy a poner el fondo blanco para que se vea el shader - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - - while ( ! done ) { - //Render to the texture - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_SetRenderTarget(renderer, texTarget); - SDL_RenderClear(renderer); - - // Pintamos MAX_SPRITES UWOLes - struct sprite* spr; - for(int i=0;i < MAX_SPRITES;++i) { - spr = &(sprites[i]); - spr->x += spr->vx; - if(spr->x < 0) { - spr->x = 0; - spr->vx *= -1; - } - if(spr->x > WORLD_WIDTH - targetRect.w) { - spr->x = (float) (WORLD_WIDTH - targetRect.w); - spr->vx *= -1; - } - spr->y += spr->vy; - if(spr->y < 0) { - spr->y = 0; - spr->vy *= -1; - } - if(spr->y > WORLD_HEIGHT - targetRect.h) { - spr->y = (float) (WORLD_HEIGHT - targetRect.h); - spr->vy *= -1; - } - - targetRect.x = (int)spr->x; - targetRect.y = (int)spr->y; - - SDL_RenderCopy(renderer, bmpTex, NULL, &targetRect); - } - - presentBackBuffer(renderer, win, texTarget, programId); - - /* This could go in a separate function */ - SDL_Event event; - while ( SDL_PollEvent(&event) ) { - if ( event.type == SDL_QUIT ) { - done = 1; - } - if ( event.type == SDL_KEYDOWN ) { - if ( event.key.keysym.sym == SDLK_SPACE ) { - useShader ^= 1; - std::cout << "useShader = " << (useShader ? "true" : "false") << std::endl; - } - if ( event.key.keysym.sym == SDLK_ESCAPE ) { - done = 1; - } - } - } + SDL_RenderCopy(renderer, bmpTex, NULL, NULL); + + shader::render(); } - SDL_DestroyTexture(texTarget); - SDL_DestroyTexture(bmpTex); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(win); SDL_Quit(); return 0; } \ No newline at end of file diff --git a/pang.bmp b/pang.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c017e9a69f70e96f77ac0b24ff30c120e4f5aeda GIT binary patch literal 276534 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
P` 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