diff --git a/release/icon.icns b/release/icon.icns index 590dbce..4c2689e 100644 Binary files a/release/icon.icns and b/release/icon.icns differ diff --git a/release/icon.ico b/release/icon.ico index 5eac2af..1f553cc 100644 Binary files a/release/icon.ico and b/release/icon.ico differ diff --git a/release/icon.png b/release/icon.png index 1b0fc70..b50269e 100644 Binary files a/release/icon.png and b/release/icon.png differ diff --git a/release/create_icons.py b/release/icon/create_icons.py similarity index 100% rename from release/create_icons.py rename to release/icon/create_icons.py diff --git a/release/icon/icon.afdesign b/release/icon/icon.afdesign index 6232a6b..8598bcf 100644 Binary files a/release/icon/icon.afdesign and b/release/icon/icon.afdesign differ diff --git a/release/icon/icon.png b/release/icon/icon.png deleted file mode 100644 index 8525269..0000000 Binary files a/release/icon/icon.png and /dev/null differ diff --git a/release/icon/icon.pxd b/release/icon/icon.pxd new file mode 100644 index 0000000..e18d5f1 Binary files /dev/null and b/release/icon/icon.pxd differ diff --git a/release/icon2.png b/release/icon2.png deleted file mode 100644 index 5afcdf7..0000000 Binary files a/release/icon2.png and /dev/null differ diff --git a/release/icon_a.pxd b/release/icon_a.pxd deleted file mode 100644 index dfdd5bf..0000000 Binary files a/release/icon_a.pxd and /dev/null differ diff --git a/release/icon_b.pxd b/release/icon_b.pxd deleted file mode 100644 index 3a9fe76..0000000 Binary files a/release/icon_b.pxd and /dev/null differ diff --git a/shaders/water.frag.glsl b/shaders/water.glsl similarity index 66% rename from shaders/water.frag.glsl rename to shaders/water.glsl index 285c29b..97495dd 100644 --- a/shaders/water.frag.glsl +++ b/shaders/water.glsl @@ -1,5 +1,5 @@ // Name: Water -// Author: diatribes +// Author: diatribes (FabriceNeyret2) #version 330 core precision highp float; @@ -14,18 +14,22 @@ uniform float iTime; thanks!! :D If it doesn't display correctly, change line 17 "r/r" to "vec3(1)" - */ void mainImage( out vec4 o, vec2 u ) { - float s=.002,i,n; + float s=.002, i=0., n; // FIXED: Initialize i=0 vec3 r = vec3(iResolution.xy, iResolution.x/iResolution.y); vec3 p = vec3(0); u = (u-r.xy/2.)/r.y-.3; - for(o *= i; i++ < 32. && s > .001;o += vec4(5,2,1,0)/max(length(u-.1), 0.001)) - for (p += vec3(u*s,s),s = 1. + p.y, - n =.01; n < 1.;n+=n) + + o = vec4(0); // FIXED: Initialize output to black + + for(; i < 32. && s > .001; i++) { + o += vec4(5,2,1,0)/max(length(u-.1), 0.001); + for (p += vec3(u*s,s), s = 1. + p.y, n =.01; n < 1.; n+=n) { s += abs(dot(sin(p.z+iTime+p / n), vec3(1))) * n*.1; + } + } o = tanh(o/5e2); } diff --git a/src/main.cpp b/src/main.cpp index f265839..5c49ce9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,6 +34,32 @@ struct DisplayMonitor { int refresh_rate = 0; }; +// Forward declarations of structs +struct ShaderMetadata { + std::string name; + std::string author; + std::string iChannel0; // "BufferA", "BufferB", "none", etc. + std::string iChannel1; + std::string iChannel2; + std::string iChannel3; +}; + +struct ShaderBuffer { + GLuint program = 0; // Shader program for this buffer + GLuint fbo = 0; // Framebuffer object + GLuint texture = 0; // Output texture + std::string name; // "BufferA", "BufferB", etc. +}; + +struct ShaderPass { + std::string shaderName; // Base name (e.g., "water") + std::string displayName; // Custom name from metadata + std::string author; // Author from metadata + GLuint imageProgram = 0; // Main image shader program + std::vector buffers; // BufferA, BufferB, etc. + ShaderMetadata metadata; // iChannel configuration +}; + // Globales simplificados (tu proyecto puede integrarlo en clases) static DisplayMonitor display_monitor_; static SDL_Window* window_ = nullptr; @@ -52,6 +78,12 @@ static std::vector shader_passes_; static int current_window_width_ = 0; static int current_window_height_ = 0; +// Self-feedback system (for shaders that use their own output as input) +static GLuint feedback_fbo_ = 0; +static GLuint feedback_texture_ = 0; +static bool current_shader_uses_feedback_ = false; +static int feedback_channel_ = -1; // Which iChannel (0-3) is used for feedback + // FPS tracking static Uint32 fps_frame_count_ = 0; static Uint32 fps_last_update_ticks_ = 0; @@ -83,31 +115,6 @@ static bool loadFileToString(const std::filesystem::path& path, std::string& out return true; } -struct ShaderMetadata { - std::string name; - std::string author; - std::string iChannel0; // "BufferA", "BufferB", "none", etc. - std::string iChannel1; - std::string iChannel2; - std::string iChannel3; -}; - -struct ShaderBuffer { - GLuint program = 0; // Shader program for this buffer - GLuint fbo = 0; // Framebuffer object - GLuint texture = 0; // Output texture - std::string name; // "BufferA", "BufferB", etc. -}; - -struct ShaderPass { - std::string shaderName; // Base name (e.g., "water") - std::string displayName; // Custom name from metadata - std::string author; // Author from metadata - GLuint imageProgram = 0; // Main image shader program - std::vector buffers; // BufferA, BufferB, etc. - ShaderMetadata metadata; // iChannel configuration -}; - static std::string trimString(const std::string& str) { size_t start = str.find_first_not_of(" \t\r\n"); size_t end = str.find_last_not_of(" \t\r\n"); @@ -325,6 +332,171 @@ static bool resizeBuffersIfNeeded(ShaderPass& pass, int width, int height) { return true; } +// ===== Self-Feedback System ===== + +static bool createFeedbackFBO(int width, int height) { + // Delete existing if any + if (feedback_fbo_ != 0) glDeleteFramebuffers(1, &feedback_fbo_); + if (feedback_texture_ != 0) glDeleteTextures(1, &feedback_texture_); + + // Create texture + glGenTextures(1, &feedback_texture_); + glBindTexture(GL_TEXTURE_2D, feedback_texture_); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Clear to black initially + std::vector black(width * height * 4, 0.0f); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, black.data()); + glBindTexture(GL_TEXTURE_2D, 0); + + // Create FBO + glGenFramebuffers(1, &feedback_fbo_); + glBindFramebuffer(GL_FRAMEBUFFER, feedback_fbo_); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, feedback_texture_, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + Logger::error("Feedback FBO creation failed: " + std::to_string(status)); + return false; + } + + Logger::info("Created feedback FBO (" + std::to_string(width) + "x" + std::to_string(height) + ")"); + return true; +} + +static void destroyFeedbackFBO() { + if (feedback_fbo_ != 0) { + glDeleteFramebuffers(1, &feedback_fbo_); + feedback_fbo_ = 0; + } + if (feedback_texture_ != 0) { + glDeleteTextures(1, &feedback_texture_); + feedback_texture_ = 0; + } + current_shader_uses_feedback_ = false; + feedback_channel_ = -1; +} + +static int detectFeedbackChannel(const ShaderMetadata& metadata) { + // Check which iChannel uses "self" feedback + if (metadata.iChannel0 == "self") return 0; + if (metadata.iChannel1 == "self") return 1; + if (metadata.iChannel2 == "self") return 2; + if (metadata.iChannel3 == "self") return 3; + return -1; // No feedback +} + +// ===== Multi-pass Shader Loading ===== + +// Forward declarations +static GLuint compileShader(GLenum type, const char* src); +static GLuint linkProgram(GLuint vs, GLuint fs); + +static std::vector findBufferFiles(const std::filesystem::path& imagePath) { + std::vector buffers; + std::filesystem::path dir = imagePath.parent_path(); + std::string basename = imagePath.stem().stem().string(); // Remove .image.glsl -> get base name + + // Check for BufferA, BufferB, BufferC, BufferD + std::vector bufferNames = {"bufferA", "bufferB", "bufferC", "bufferD"}; + for (const auto& bufName : bufferNames) { + std::filesystem::path bufferPath = dir / (basename + "." + bufName + ".glsl"); + if (std::filesystem::exists(bufferPath)) { + buffers.push_back(bufName); + Logger::info("Found buffer: " + bufferPath.string()); + } + } + + return buffers; +} + +static GLuint loadMultiPassShader(const std::filesystem::path& imagePath, ShaderPass& outPass, int width, int height) { + std::string basename = imagePath.stem().stem().string(); + outPass.shaderName = basename; + + // Load and compile Image shader + std::string imageSrc; + if (!loadFileToString(imagePath, imageSrc)) { + Logger::error("Failed to load image shader: " + imagePath.string()); + return 0; + } + + // Extract metadata from Image shader + outPass.metadata = extractShaderMetadata(imageSrc); + outPass.displayName = outPass.metadata.name.empty() ? basename : outPass.metadata.name; + outPass.author = outPass.metadata.author; + + GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc); + GLuint fs = compileShader(GL_FRAGMENT_SHADER, imageSrc.c_str()); + + if (!vs || !fs) { + if (vs) glDeleteShader(vs); + if (fs) glDeleteShader(fs); + return 0; + } + + outPass.imageProgram = linkProgram(vs, fs); + glDeleteShader(vs); + glDeleteShader(fs); + + if (!outPass.imageProgram) { + return 0; + } + + // Find and load buffer shaders + std::vector bufferNames = findBufferFiles(imagePath); + std::filesystem::path dir = imagePath.parent_path(); + + for (const auto& bufName : bufferNames) { + ShaderBuffer buffer; + buffer.name = bufName; + + // Load buffer shader source + std::filesystem::path bufferPath = dir / (basename + "." + bufName + ".glsl"); + std::string bufferSrc; + if (!loadFileToString(bufferPath, bufferSrc)) { + Logger::error("Failed to load buffer: " + bufferPath.string()); + continue; + } + + // Compile buffer shader + GLuint bufVs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc); + GLuint bufFs = compileShader(GL_FRAGMENT_SHADER, bufferSrc.c_str()); + + if (!bufVs || !bufFs) { + if (bufVs) glDeleteShader(bufVs); + if (bufFs) glDeleteShader(bufFs); + continue; + } + + buffer.program = linkProgram(bufVs, bufFs); + glDeleteShader(bufVs); + glDeleteShader(bufFs); + + if (!buffer.program) { + continue; + } + + // Create FBO and texture for this buffer + if (!createBufferFBO(buffer, width, height)) { + glDeleteProgram(buffer.program); + continue; + } + + outPass.buffers.push_back(buffer); + Logger::info("Loaded buffer: " + bufName); + } + + Logger::info("Multi-pass shader loaded: " + outPass.displayName + " (" + std::to_string(outPass.buffers.size()) + " buffers)"); + return outPass.imageProgram; +} + static void updateWindowTitle() { if (!window_ || shader_list_.empty()) return; @@ -404,7 +576,7 @@ static GLuint loadAndCompileShader(size_t index) { return 0; } - // Extract custom shader metadata (name and author) from source code + // Extract custom shader metadata (name, author, iChannels) from source code ShaderMetadata metadata = extractShaderMetadata(fragSrc); if (!metadata.name.empty()) { shader_names_[index] = metadata.name; @@ -415,6 +587,14 @@ static GLuint loadAndCompileShader(size_t index) { Logger::info("Shader author: " + metadata.author); } + // Detect self-feedback + feedback_channel_ = detectFeedbackChannel(metadata); + current_shader_uses_feedback_ = (feedback_channel_ >= 0); + + if (current_shader_uses_feedback_) { + Logger::info("Shader uses self-feedback on iChannel" + std::to_string(feedback_channel_)); + } + GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc); GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragSrc.c_str()); @@ -527,6 +707,9 @@ void switchShader(int direction) { glDeleteProgram(current_program_); } + // Destroy feedback FBO from previous shader + destroyFeedbackFBO(); + current_program_ = new_program; current_shader_index_ = new_index; shader_start_ticks_ = SDL_GetTicks(); @@ -765,25 +948,76 @@ int main(int argc, char** argv) { int w, h; SDL_GetWindowSize(window_, &w, &h); - glViewport(0, 0, w, h); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); + + // Create/resize feedback FBO if needed + if (current_shader_uses_feedback_) { + if (feedback_fbo_ == 0 || current_window_width_ != w || current_window_height_ != h) { + createFeedbackFBO(w, h); + current_window_width_ = w; + current_window_height_ = h; + } + } glUseProgram(current_program_); - // Obtener uniform locations (se recalculan porque el shader puede cambiar) + // Obtener uniform locations 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() - shader_start_ticks_) / 1000.0f; - glUniform1f(locTime, t); - } + float t = (SDL_GetTicks() - shader_start_ticks_) / 1000.0f; - glBindVertexArray(vao); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - glBindVertexArray(0); + // === FEEDBACK RENDERING === + if (current_shader_uses_feedback_) { + // Step 1: Bind feedback texture to iChannel + std::string channelName = "iChannel" + std::to_string(feedback_channel_); + GLint locChannel = glGetUniformLocation(current_program_, channelName.c_str()); + + if (locChannel >= 0) { + glActiveTexture(GL_TEXTURE0 + feedback_channel_); + glBindTexture(GL_TEXTURE_2D, feedback_texture_); + glUniform1i(locChannel, feedback_channel_); + } + + // Step 2: Render to feedback FBO + glBindFramebuffer(GL_FRAMEBUFFER, feedback_fbo_); + glViewport(0, 0, w, h); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + if (locRes >= 0) glUniform2f(locRes, float(w), float(h)); + if (locTime >= 0) glUniform1f(locTime, t); + + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); + + // Step 3: Render to screen (using the same FBO texture) + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, w, h); + + if (locRes >= 0) glUniform2f(locRes, float(w), float(h)); + if (locTime >= 0) glUniform1f(locTime, t); + + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); + + // Unbind texture + glActiveTexture(GL_TEXTURE0 + feedback_channel_); + glBindTexture(GL_TEXTURE_2D, 0); + } else { + // === NORMAL RENDERING (no feedback) === + glViewport(0, 0, w, h); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + if (locRes >= 0) glUniform2f(locRes, float(w), float(h)); + if (locTime >= 0) glUniform1f(locTime, t); + + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); + } SDL_GL_SwapWindow(window_); if (!Options_video.vsync) { @@ -798,6 +1032,9 @@ int main(int argc, char** argv) { glDeleteProgram(current_program_); } + // Cleanup feedback FBO + destroyFeedbackFBO(); + // Cleanup audio if (current_music_) { JA_DeleteMusic(current_music_);