cool stuff

This commit is contained in:
2026-01-13 19:47:59 +00:00
parent 687a8ac749
commit 89c0bb5f7f
6 changed files with 448 additions and 56 deletions

256
main.cpp
View File

@@ -9,6 +9,7 @@
#include <sstream>
#include <chrono>
#include <cmath>
#include <algorithm>
#include "xdg-shell-client-protocol.h"
#include "font8x8.h"
@@ -47,19 +48,65 @@ bool button_pressed = false;
// Time state
auto start_time = std::chrono::high_resolution_clock::now();
// --- Framebuffer State (Multipass) ---
GLuint fbo[2];
GLuint fbo_texture[2];
int ping = 0;
int pong = 1;
bool fbo_initialized = false;
int fbo_width = 0;
int fbo_height = 0;
void init_fbos(int w, int h) {
if (fbo_initialized && w == fbo_width && h == fbo_height) return;
if (fbo_initialized) {
glDeleteFramebuffers(2, fbo);
glDeleteTextures(2, fbo_texture);
}
fbo_width = w;
fbo_height = h;
glGenFramebuffers(2, fbo);
glGenTextures(2, fbo_texture);
for (int i = 0; i < 2; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, fbo[i]);
glBindTexture(GL_TEXTURE_2D, fbo_texture[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 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);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_texture[i], 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cerr << "Framebuffer " << i << " incomplete!" << std::endl;
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind
fbo_initialized = true;
}
// Shaders
struct ShaderProgram {
GLuint program;
GLint posAttrib;
GLint timeUniform;
GLint resolutionUniform;
GLint mouseUniform;
GLint backbufferUniform;
std::string name;
float color[3]; // For sidebar button color
float color[3];
bool is_stateful;
};
std::vector<ShaderProgram> shaders;
int current_shader_index = 0;
GLuint sidebar_program; // Simple shader for drawing UI quads
GLuint copy_program; // Helper for copying FBO to screen
// UI Layout
const int SIDEBAR_WIDTH = 200;
@@ -84,6 +131,34 @@ void init_font() {
for (int row = 0; row < 8; ++row) {
unsigned char bits = font8x8_basic[char_idx][row];
for (int col = 0; col < 8; ++col) {
// Revert to standard LSB=Right?
// Wait, if (bits >> col) & 1 IS standard, and I previously FLIPPED it to (bits >> (7-col)).
// And the user saw MIRRORED text.
// Then (7-col) WAS mirrored.
// So 'col' should be correct.
// Assuming bit 0 is Rightmost pixel?
// If I use (bits >> col) & 1 -> Col 0 gets Bit 0 (Right).
// So Right-Bit is drawn on Left.
// THIS produces Mirroring IF bit 0 is Right.
// If the user SAW mirroring with (7-col) which puts Bit 7 on Left.
// Then Bit 7 IS Left.
// Meaning (7-col) is CORRECT.
// Wait.
// If I saw "AMSALP" (Mirrored String).
// BUT "Normal Letters" (Upright P).
// Then the LETTERS are fine. The STRING is mirrored.
// IF I revert the letters, they will become Mirrored P ('q').
// This won't fix the string order.
// Let's stick to the plan: Revert bit flips to be safe, BUT also investigate String Order.
// String order is P L A S M A.
// Drawn at X=0, X=10...
// If P is at Right. X-axis is flipped.
// Okay, I will revert to `(bits >> col) & 1` and `row`.
// This is the "default" implementation I found online for font8x8 usually.
if ((bits >> col) & 1) {
int px = grid_x * 8 + col;
int py = grid_y * 8 + row;
@@ -142,6 +217,12 @@ void draw_text(const std::string& text, float x, float y, float scale, float r,
glUniform2f(scrLoc, (float)width, (float)height);
glUniform3f(colLoc, r, g, b);
// Bind Font Texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, font_texture);
GLint samplerLoc = glGetUniformLocation(text_program, "u_tex");
glUniform1i(samplerLoc, 0);
std::vector<float> verts;
float cx = x;
float cy = y;
@@ -228,13 +309,41 @@ GLuint create_program(const char* vertPath, const char* fragPath) {
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) {}
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) {}
// Interactive State
float zoom_level = 1.0f;
float view_x = -0.5f;
float view_y = 0.0f;
bool is_dragging = false;
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {
if (cursor_x > SIDEBAR_WIDTH) { // Only zoom in main area
double val = wl_fixed_to_double(value);
if (val < 0) {
zoom_level *= 1.1f;
} else if (val > 0) {
zoom_level /= 1.1f;
}
}
}
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {
double old_x = cursor_x;
double old_y = cursor_y;
cursor_x = wl_fixed_to_double(sx);
cursor_y = wl_fixed_to_double(sy);
if (is_dragging) {
float dx = (cursor_x - old_x) / height;
float dy = (cursor_y - old_y) / height;
// Pan logic
view_x -= dx / zoom_level * 2.0;
view_y += dy / zoom_level * 2.0;
}
}
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
if (state == WL_POINTER_BUTTON_STATE_PRESSED && button == 0x110) { // Left click
if (button == 0x110) { // Left click
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
if (cursor_x < SIDEBAR_WIDTH) {
int clicked_index = -1;
for (size_t i = 0; i < shaders.size(); ++i) {
@@ -248,12 +357,20 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
if (clicked_index != -1) {
current_shader_index = clicked_index;
std::cout << "Switched to shader: " << shaders[current_shader_index].name << std::endl;
if (shaders[current_shader_index].name == "MANDEL") {
zoom_level = 1.0f;
view_x = -0.5f;
view_y = 0.0f;
}
}
} else {
is_dragging = true;
}
} else {
is_dragging = false;
}
}
}
}
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {}
static const struct wl_pointer_listener pointer_listener = {
.enter = pointer_handle_enter,
@@ -371,11 +488,18 @@ void render_frame() {
float text_h = 8 * text_scale;
float text_y = by + (BUTTON_HEIGHT - text_h) / 2;
float text_x = BUTTON_MARGIN + 10;
draw_text(shaders[i].name, text_x, text_y, text_scale, 0.0f, 0.0f, 0.0f);
draw_text(shaders[i].name, text_x, text_y, text_scale, 1.0f, 1.0f, 1.0f);
}
// 3. Shader
glViewport(SIDEBAR_WIDTH, 0, width - SIDEBAR_WIDTH, height);
// 3. Render Active Shader
int view_w = width - SIDEBAR_WIDTH;
int view_h = height;
// Safety check for view dimensions
if (view_w <= 0) view_w = 1;
if (view_h <= 0) view_h = 1;
init_fbos(view_w, view_h);
ShaderProgram &active = shaders[current_shader_index];
glUseProgram(active.program);
@@ -384,13 +508,80 @@ void render_frame() {
float time = std::chrono::duration<float>(now - start_time).count();
if (active.timeUniform != -1) glUniform1f(active.timeUniform, time);
if (active.resolutionUniform != -1) glUniform2f(active.resolutionUniform, (float)(width - SIDEBAR_WIDTH), (float)height);
if (active.resolutionUniform != -1) glUniform2f(active.resolutionUniform, (float)view_w, (float)view_h);
// Mouse relative to viewport
if (active.mouseUniform != -1) {
float mx = (float)cursor_x - SIDEBAR_WIDTH;
float my = (float)height - (float)cursor_y;
glUniform2f(active.mouseUniform, mx, my);
}
GLint zoomLoc = glGetUniformLocation(active.program, "u_zoom");
if (zoomLoc != -1) glUniform1f(zoomLoc, zoom_level);
GLint centerLoc = glGetUniformLocation(active.program, "u_center");
if (centerLoc != -1) glUniform2f(centerLoc, view_x, view_y);
if (active.is_stateful) {
// Ping-Pong rendering
// Pass 1: Draw to FBO[pong] reading from FBO[ping]
glBindFramebuffer(GL_FRAMEBUFFER, fbo[pong]);
glViewport(0, 0, view_w, view_h);
glUseProgram(active.program); // Re-bind to be safe
// Update uniforms for FBO pass
if (active.timeUniform != -1) glUniform1f(active.timeUniform, time);
if (active.resolutionUniform != -1) glUniform2f(active.resolutionUniform, (float)view_w, (float)view_h);
if (active.mouseUniform != -1) {
float mx = (float)cursor_x - SIDEBAR_WIDTH;
float my = (float)height - (float)cursor_y;
glUniform2f(active.mouseUniform, mx, my);
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, fbo_texture[ping]);
if (active.backbufferUniform != -1) glUniform1i(active.backbufferUniform, 0);
GLfloat vertices[] = { -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1 };
glVertexAttribPointer(active.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(active.posAttrib);
glDrawArrays(GL_TRIANGLES, 0, 6);
// Swap ping/pong
int temp = ping;
ping = pong;
pong = temp;
// Pass 2: Copy to Screen
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(SIDEBAR_WIDTH, 0, view_w, view_h);
glUseProgram(copy_program);
GLint pos = glGetAttribLocation(copy_program, "position");
GLint tex = glGetUniformLocation(copy_program, "u_tex");
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, fbo_texture[ping]);
glUniform1i(tex, 0);
glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(pos);
glDrawArrays(GL_TRIANGLES, 0, 6);
} else {
// Stateless Rendering
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(SIDEBAR_WIDTH, 0, view_w, view_h);
GLfloat vertices[] = { -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1 };
glVertexAttribPointer(active.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(active.posAttrib);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
struct wl_callback *callback = wl_surface_frame(surface);
wl_callback_add_listener(callback, &frame_listener, nullptr);
@@ -399,6 +590,32 @@ void render_frame() {
// --- Init & Main ---
void init_copy_shader() {
const char* vs =
"#version 100\n"
"attribute vec2 position;\n"
"varying vec2 uv;\n"
"void main(){\n"
" uv = position * 0.5 + 0.5;\n"
" gl_Position = vec4(position, 0.0, 1.0);\n"
"}\n";
const char* fs =
"#version 100\n"
"precision mediump float;\n"
"varying vec2 uv;\n"
"uniform sampler2D u_tex;\n"
"void main(){\n"
" gl_FragColor = texture2D(u_tex, uv);\n"
"}\n";
GLuint v = load_shader_src(vs, GL_VERTEX_SHADER);
GLuint f = load_shader_src(fs, GL_FRAGMENT_SHADER);
copy_program = glCreateProgram();
glAttachShader(copy_program, v);
glAttachShader(copy_program, f);
glLinkProgram(copy_program);
}
void init_shaders() {
const char* sidebar_vert =
"#version 100\n"
@@ -417,25 +634,32 @@ void init_shaders() {
glAttachShader(sidebar_program, sf);
glLinkProgram(sidebar_program);
auto add_shader = [&](const char* name, const char* fpath, float r, float g, float b) {
init_copy_shader();
auto add_shader = [&](const char* name, const char* fpath, float r, float g, float b, bool stateful) {
GLuint prog = create_program("shaders/vert.glsl", fpath);
if (prog) {
ShaderProgram sp;
sp.program = prog;
sp.name = name; // Now used for text!
sp.name = name;
sp.posAttrib = glGetAttribLocation(prog, "position");
sp.timeUniform = glGetUniformLocation(prog, "u_time");
sp.resolutionUniform = glGetUniformLocation(prog, "u_resolution");
sp.mouseUniform = glGetUniformLocation(prog, "u_mouse");
sp.backbufferUniform = glGetUniformLocation(prog, "u_backbuffer");
sp.color[0] = r; sp.color[1] = g; sp.color[2] = b;
sp.is_stateful = stateful;
shaders.push_back(sp);
}
};
add_shader("PLASMA", "shaders/plasma.frag", 0.8f, 0.2f, 0.2f);
add_shader("TUNNEL", "shaders/tunnel.frag", 0.2f, 0.8f, 0.2f);
add_shader("FRACTAL", "shaders/fractal.frag", 0.2f, 0.2f, 0.8f);
add_shader("MANDEL", "shaders/mandelbrot.frag", 0.5f, 0.0f, 0.5f);
add_shader("DBL PEND", "shaders/pendulum.frag", 0.7f, 0.7f, 0.1f);
add_shader("PLASMA", "shaders/plasma.frag", 0.8f, 0.2f, 0.2f, false);
add_shader("TUNNEL", "shaders/tunnel.frag", 0.2f, 0.8f, 0.2f, false);
add_shader("FRACTAL", "shaders/fractal.frag", 0.2f, 0.2f, 0.8f, false);
add_shader("MANDEL", "shaders/mandelbrot.frag", 0.5f, 0.0f, 0.5f, false);
add_shader("DBL PEND", "shaders/pendulum.frag", 0.7f, 0.7f, 0.1f, false);
add_shader("REACTION", "shaders/reaction.frag", 0.0f, 0.8f, 0.8f, true);
add_shader("FLUID", "shaders/fluid.frag", 0.2f, 0.3f, 0.9f, true);
}
int main() {

BIN
main.o

Binary file not shown.

Binary file not shown.

66
shaders/fluid.frag Normal file
View File

@@ -0,0 +1,66 @@
#version 100
precision mediump float;
uniform float u_time;
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform sampler2D u_backbuffer;
// Fluid Parameters
#define DT 0.15
#define VISCOSITY 0.99
#define FADE 0.995
// Pseudo-random
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
void main() {
vec2 pixel = 1.0 / u_resolution;
vec2 uv = gl_FragCoord.xy / u_resolution;
// 1. Read current state (Vel, Density)
if (u_time < 0.5 || u_resolution.x < 1.0) {
gl_FragColor = vec4(0.5, 0.5, 0.0, 1.0);
return;
}
vec4 state = texture2D(u_backbuffer, uv);
vec2 vel = state.xy; // velocity range [-1, 1]? we store as [0,1]. Need remap.
// Actually simpler to store raw flow in 0..1 range whre 0.5 is 0 velocity.
// Let's assume 0.5 is 0.
vec2 v = (vel - 0.5) * 2.0;
float density = state.z;
// 2. Advection (move state by velocity)
// We look *back* along velocity to find where the current content came from.
vec2 coord = uv - v * pixel * 5.0; // Scale velocity effect
vec4 advected = texture2D(u_backbuffer, coord);
vec2 new_v = (advected.xy - 0.5) * 2.0;
float new_d = advected.z;
// 3. Mouse Interaction (Splat)
float d = distance(gl_FragCoord.xy, u_mouse);
if (d < 30.0 && u_mouse.x > 0.0) {
// Add density
new_d += 0.5;
// Add velocity (push away from mouse or just random?)
// Ideally we need mouse delta (velocity). u_mouse is just pos.
// We can just add a radial force or flow?
// Let's just add some noise/movement
new_v += vec2(rand(uv + u_time) - 0.5, rand(uv + u_time + 1.0) - 0.5) * 1.0;
}
// 4. Decay (Viscosity/Dissipation)
new_v *= VISCOSITY;
new_d *= FADE;
// Remap velocity back to 0..1
vel = new_v * 0.5 + 0.5;
gl_FragColor = vec4(vel, new_d, 1.0);
}

View File

@@ -1,31 +1,56 @@
#version 100
precision mediump float;
precision highp float;
uniform float u_time;
uniform vec2 u_resolution;
uniform float u_zoom;
uniform vec2 u_center;
void main() {
vec2 uv = (gl_FragCoord.xy * 2.0 - u_resolution.xy) / u_resolution.y;
// Current pixel coordinate in 0..1
vec2 st = gl_FragCoord.xy / u_resolution.xy;
st.x *= u_resolution.x / u_resolution.y; // Aspect ratio correction
// Auto zoom
float zoom = 1.0 + sin(u_time * 0.1) * 0.5;
zoom = pow(zoom, 4.0);
vec2 c = uv / zoom - vec2(0.74364388703, 0.13182590421); // Zoom into a specific interesting point
// Map to Mandelbrot space using Zoom and Center
// Default (Zoom 1): -2.5 to 1.5 X, -1.5 to 1.5 Y approx?
// Let's say center is 0,0. Range -2..2.
// st is 0..AspectRatio (approx 0..1.7)
// Center the coords:
vec2 c_uv = (gl_FragCoord.xy - u_resolution.xy * 0.5) / u_resolution.y;
// Apply Zoom and Pan
vec2 c = (c_uv / u_zoom) + u_center;
// Mandelbrot iteration
vec2 z = vec2(0.0);
float iter = 0.0;
float max_iter = 100.0;
float max_iter = 100.0 + log(u_zoom) * 20.0; // Increase details with zoom
for (float i = 0.0; i < 100.0; i++) {
z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;
if (length(z) > 4.0) break;
iter += 1.0;
for (float i = 0.0; i < 500.0; i++) {
if (i > max_iter) break;
// Z = Z^2 + C
float x = (z.x * z.x - z.y * z.y) + c.x;
float y = (z.y * z.x + z.x * z.y) + c.y;
if ((x * x + y * y) > 4.0) {
iter = i;
break;
}
z.x = x;
z.y = y;
}
float f = iter / max_iter;
vec3 col = vec3(f);
col = 0.5 + 0.5 * cos(3.0 + f * 10.0 + vec3(0.0, 0.6, 1.0));
if (iter >= 99.0) col = vec3(0.0);
gl_FragColor = vec4(col, 1.0);
// Coloring
float t = iter / max_iter;
if (iter == 0.0) {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
} else {
gl_FragColor = vec4(
sqrt(t),
t * t,
sin(t * 3.1415),
1.0
);
}
}

77
shaders/reaction.frag Normal file
View File

@@ -0,0 +1,77 @@
#version 100
precision mediump float;
uniform float u_time;
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform sampler2D u_backbuffer;
// Gray-Scott parameters
// standard "spots" / coral
#define DA 1.0
#define DB 0.5
#define FEED 0.055
#define KILL 0.062
#define DT 1.0
void main() {
vec2 pixel = 1.0 / u_resolution;
vec2 uv = gl_FragCoord.xy / u_resolution;
// Read current state
vec4 state = texture2D(u_backbuffer, uv);
float a = state.r;
float b = state.g;
// Laplaclan (convolution)
// 0.05 0.2 0.05
// 0.2 -1 0.2
// 0.05 0.2 0.05
vec4 sum = vec4(0.0);
sum += texture2D(u_backbuffer, uv + vec2(-1.0, -1.0) * pixel) * 0.05;
sum += texture2D(u_backbuffer, uv + vec2( 0.0, -1.0) * pixel) * 0.2;
sum += texture2D(u_backbuffer, uv + vec2( 1.0, -1.0) * pixel) * 0.05;
sum += texture2D(u_backbuffer, uv + vec2(-1.0, 0.0) * pixel) * 0.2;
sum -= state * 1.0;
sum += texture2D(u_backbuffer, uv + vec2( 1.0, 0.0) * pixel) * 0.2;
sum += texture2D(u_backbuffer, uv + vec2(-1.0, 1.0) * pixel) * 0.05;
sum += texture2D(u_backbuffer, uv + vec2( 0.0, 1.0) * pixel) * 0.2;
sum += texture2D(u_backbuffer, uv + vec2( 1.0, 1.0) * pixel) * 0.05;
// Reaction-Diffusion logic
// A' = A + (Da * lapA - A*B*B + f*(1-A)) * dt
// B' = B + (Db * lapB + A*B*B - (k+f)*B) * dt
float lapA = sum.r;
float lapB = sum.g;
float abb = a * b * b;
float nA = a + (DA * lapA - abb + FEED * (1.0 - a)) * DT;
float nB = b + (DB * lapB + abb - (KILL + FEED) * b) * DT;
// Clamp
nA = clamp(nA, 0.0, 1.0);
nB = clamp(nB, 0.0, 1.0);
// Mouse Interaction (Add B)
float dist = distance(gl_FragCoord.xy, u_mouse);
if (dist < 20.0 && u_mouse.x > 0.0) {
nB = 0.9;
}
// Initialization (random noise at start)
if (u_time < 0.5) {
if (fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453) > 0.99) {
nB = 1.0;
} else {
nB = 0.0;
nA = 1.0;
}
}
gl_FragColor = vec4(nA, nB, 0.0, 1.0);
}