diff --git a/src/defines.hpp b/src/defines.hpp new file mode 100644 index 0000000..f31b3f7 --- /dev/null +++ b/src/defines.hpp @@ -0,0 +1,12 @@ +#pragma once + +// Nombre de la aplicación +constexpr const char* APP_NAME = "Shadertoy"; + +// Tamaño de ventana por defecto +constexpr int WINDOW_WIDTH = 800; +constexpr int WINDOW_HEIGHT = 800; + +// Rutas +constexpr const char* SHADERS_FOLDER = "shaders"; +constexpr const char* DEFAULT_SHADER = "shaders/test.frag.glsl"; diff --git a/src/main.cpp b/src/main.cpp index cfca0ef..ef9c8dd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,11 +4,14 @@ #include #include #include +#include #include #include #include #include +#include "defines.hpp" + // Simple logger compatible con el estilo que usas struct Logger { static void info(const std::string& s) { std::cout << "[INFO] " << s << '\n'; } @@ -32,12 +35,12 @@ struct DisplayMonitor { static DisplayMonitor display_monitor_; static SDL_Window* window_ = nullptr; -// Constante por defecto del fragment shader -static constexpr const char* DEFAULT_FRAG = "shaders/test.frag.glsl"; - -// Tamaño de ventana por defecto -static constexpr int WINDOW_WIDTH = 800; -static constexpr int WINDOW_HEIGHT = 800; +// Sistema de shaders +static std::vector shader_list_; +static size_t current_shader_index_ = 0; +static std::filesystem::path shaders_directory_; +static GLuint current_program_ = 0; +static Uint32 shader_start_ticks_ = 0; // Vertex shader embebido static const char* vertexShaderSrc = R"glsl( @@ -60,6 +63,38 @@ static bool loadFileToString(const std::filesystem::path& path, std::string& out return true; } +static std::vector scanShaderDirectory(const std::filesystem::path& directory) { + std::vector shaders; + + if (!std::filesystem::exists(directory) || !std::filesystem::is_directory(directory)) { + Logger::error("Shader directory does not exist: " + directory.string()); + return shaders; + } + + for (const auto& entry : std::filesystem::directory_iterator(directory)) { + if (entry.is_regular_file()) { + auto ext = entry.path().extension().string(); + if (ext == ".glsl") { + shaders.push_back(entry.path()); + } + } + } + + // Ordenar alfabéticamente + std::sort(shaders.begin(), shaders.end()); + + Logger::info("Found " + std::to_string(shaders.size()) + " shader(s) in " + directory.string()); + return shaders; +} + +static void updateWindowTitle() { + if (!window_ || shader_list_.empty()) return; + + std::string filename = shader_list_[current_shader_index_].filename().string(); + std::string title = std::string(APP_NAME) + " (" + filename + ")"; + SDL_SetWindowTitle(window_, title.c_str()); +} + static GLuint compileShader(GLenum type, const char* src) { GLuint s = glCreateShader(type); glShaderSource(s, 1, &src, nullptr); @@ -97,6 +132,44 @@ static GLuint linkProgram(GLuint vs, GLuint fs) { return p; } +static GLuint loadAndCompileShader(size_t index) { + if (index >= shader_list_.size()) { + Logger::error("Invalid shader index: " + std::to_string(index)); + return 0; + } + + const auto& shaderPath = shader_list_[index]; + Logger::info("Loading shader: " + shaderPath.string()); + + std::string fragSrc; + if (!loadFileToString(shaderPath, fragSrc)) { + Logger::error("Failed to load shader file: " + shaderPath.string()); + return 0; + } + + GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc); + GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragSrc.c_str()); + + if (!vs || !fs) { + if (vs) glDeleteShader(vs); + if (fs) glDeleteShader(fs); + Logger::error("Shader compilation failed for: " + shaderPath.string()); + return 0; + } + + GLuint program = linkProgram(vs, fs); + glDeleteShader(vs); + glDeleteShader(fs); + + if (!program) { + Logger::error("Program linking failed for: " + shaderPath.string()); + return 0; + } + + Logger::info("Shader loaded successfully: " + shaderPath.filename().string()); + return program; +} + // --- Funciones basadas en tu código --- void getDisplayInfo() { int num_displays = 0; @@ -148,6 +221,36 @@ void toggleFullscreen() { setFullscreenMode(); } +void switchShader(int direction) { + if (shader_list_.empty()) return; + + // Calcular nuevo índice con wrap-around cíclico + size_t new_index = current_shader_index_; + if (direction > 0) { + new_index = (current_shader_index_ + 1) % shader_list_.size(); + } else if (direction < 0) { + new_index = (current_shader_index_ == 0) ? shader_list_.size() - 1 : current_shader_index_ - 1; + } + + // Intentar cargar el nuevo shader + GLuint new_program = loadAndCompileShader(new_index); + if (new_program == 0) { + Logger::error("Failed to switch shader, keeping current one"); + return; + } + + // Éxito: eliminar programa anterior y actualizar + if (current_program_ != 0) { + glDeleteProgram(current_program_); + } + + current_program_ = new_program; + current_shader_index_ = new_index; + shader_start_ticks_ = SDL_GetTicks(); + + updateWindowTitle(); +} + // Manejo de teclas void handleDebugEvents(const SDL_Event& event) { // evitar repetición de teclas: event.key.repeat disponible en SDL3 @@ -157,6 +260,14 @@ void handleDebugEvents(const SDL_Event& event) { toggleFullscreen(); break; } + case SDLK_LEFT: { + switchShader(-1); + break; + } + case SDLK_RIGHT: { + switchShader(+1); + break; + } default: break; } @@ -172,7 +283,7 @@ int main(int argc, char** argv) { if (a == "-F" || a == "--fullscreen") { fullscreenFlag = true; continue; } if (shaderPath.empty()) shaderPath = a; } - if (shaderPath.empty()) shaderPath = DEFAULT_FRAG; + if (shaderPath.empty()) shaderPath = DEFAULT_SHADER; Options_video.fullscreen = fullscreenFlag; // Inicializar SDL3 @@ -192,7 +303,7 @@ int main(int argc, char** argv) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); // Crear ventana - window_ = SDL_CreateWindow("Shadertoy SDL3 + OpenGL", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + window_ = SDL_CreateWindow(APP_NAME, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); if (!window_) { Logger::error(std::string("SDL_CreateWindow error: ") + SDL_GetError()); SDL_Quit(); return -1; } // Aplicar fullscreen si el flag estaba activado @@ -215,41 +326,69 @@ int main(int argc, char** argv) { return -1; } - // Localizar y cargar el fragment shader (intenta rutas comunes) - std::vector candidates; - candidates.emplace_back(shaderPath); - candidates.emplace_back(std::filesystem::path("..") / shaderPath); - candidates.emplace_back(std::filesystem::path(".") / shaderPath); - if (argc > 0 && argv[0]) { - std::filesystem::path exe = argv[0]; - if (exe.has_parent_path()) { - candidates.emplace_back(exe.parent_path() / shaderPath); - candidates.emplace_back(exe.parent_path() / ".." / shaderPath); - } + // Determinar carpeta de shaders + std::filesystem::path shaderFile(shaderPath); + if (shaderFile.has_parent_path()) { + shaders_directory_ = shaderFile.parent_path(); + } else { + shaders_directory_ = SHADERS_FOLDER; } - std::string fragSrc; - std::filesystem::path found; - for (auto &p : candidates) { - if (loadFileToString(p, fragSrc)) { found = p; break; } - } - if (found.empty()) { - Logger::error("Failed to load fragment shader file. Tried paths:"); - for (auto &p : candidates) Logger::error(" " + p.string()); + // Escanear carpeta de shaders + shader_list_ = scanShaderDirectory(shaders_directory_); + if (shader_list_.empty()) { + Logger::error("No shaders found in directory: " + shaders_directory_.string()); SDL_GL_DestroyContext(glContext); SDL_DestroyWindow(window_); SDL_Quit(); return -1; } - Logger::info("Loaded fragment shader from: " + found.string()); - GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc); - GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragSrc.c_str()); - if (!vs || !fs) { SDL_GL_DestroyContext(glContext); SDL_DestroyWindow(window_); SDL_Quit(); return -1; } - GLuint program = linkProgram(vs, fs); - glDeleteShader(vs); - glDeleteShader(fs); - if (!program) { SDL_GL_DestroyContext(glContext); SDL_DestroyWindow(window_); SDL_Quit(); return -1; } + // Determinar shader inicial + size_t initial_index = 0; + bool found_shader = false; + + // Intentar encontrar el shader especificado + std::filesystem::path target_shader = shaderFile.has_parent_path() ? shaderFile : (shaders_directory_ / shaderFile.filename()); + for (size_t i = 0; i < shader_list_.size(); ++i) { + if (shader_list_[i] == target_shader) { + initial_index = i; + found_shader = true; + break; + } + } + + // Si no se encuentra, intentar con DEFAULT_SHADER + if (!found_shader) { + std::filesystem::path default_path(DEFAULT_SHADER); + for (size_t i = 0; i < shader_list_.size(); ++i) { + if (shader_list_[i] == default_path) { + initial_index = i; + found_shader = true; + break; + } + } + } + + // Si aún no se encuentra, usar el primer shader de la lista + if (!found_shader) { + Logger::info("Specified shader not found, using first shader in directory"); + initial_index = 0; + } + + // Cargar shader inicial + current_shader_index_ = initial_index; + current_program_ = loadAndCompileShader(current_shader_index_); + if (current_program_ == 0) { + Logger::error("Failed to load initial shader"); + SDL_GL_DestroyContext(glContext); + SDL_DestroyWindow(window_); + SDL_Quit(); + return -1; + } + + shader_start_ticks_ = SDL_GetTicks(); + updateWindowTitle(); // Quad setup float quadVertices[] = { @@ -270,11 +409,7 @@ int main(int argc, char** argv) { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); - GLint locRes = glGetUniformLocation(program, "iResolution"); - GLint locTime = glGetUniformLocation(program, "iTime"); - bool running = true; - Uint32 startTicks = SDL_GetTicks(); while (running) { SDL_Event e; @@ -297,10 +432,15 @@ int main(int argc, char** argv) { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); - glUseProgram(program); + glUseProgram(current_program_); + + // Obtener uniform locations (se recalculan porque el shader puede cambiar) + GLint locRes = glGetUniformLocation(current_program_, "iResolution"); + GLint locTime = glGetUniformLocation(current_program_, "iTime"); + if (locRes >= 0) glUniform2f(locRes, float(w), float(h)); if (locTime >= 0) { - float t = (SDL_GetTicks() - startTicks) / 1000.0f; + float t = (SDL_GetTicks() - shader_start_ticks_) / 1000.0f; glUniform1f(locTime, t); } @@ -315,7 +455,9 @@ int main(int argc, char** argv) { // Cleanup glDeleteBuffers(1, &vbo); glDeleteVertexArrays(1, &vao); - glDeleteProgram(program); + if (current_program_ != 0) { + glDeleteProgram(current_program_); + } SDL_GL_DestroyContext(glContext); SDL_DestroyWindow(window_);