cool stuff
This commit is contained in:
300
main.cpp
300
main.cpp
@@ -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,9 +131,37 @@ 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;
|
||||
int py = grid_y * 8 + row;
|
||||
pixels[py * tex_w + px] = 255;
|
||||
}
|
||||
}
|
||||
@@ -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,32 +309,68 @@ 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) {}
|
||||
|
||||
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {
|
||||
cursor_x = wl_fixed_to_double(sx);
|
||||
cursor_y = wl_fixed_to_double(sy);
|
||||
}
|
||||
// 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_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 (cursor_x < SIDEBAR_WIDTH) {
|
||||
int clicked_index = -1;
|
||||
for (size_t i = 0; i < shaders.size(); ++i) {
|
||||
int y_start = BUTTON_MARGIN + i * (BUTTON_HEIGHT + BUTTON_MARGIN);
|
||||
int y_end = y_start + BUTTON_HEIGHT;
|
||||
if (cursor_y >= y_start && cursor_y <= y_end && cursor_x >= BUTTON_MARGIN && cursor_x <= SIDEBAR_WIDTH - BUTTON_MARGIN) {
|
||||
clicked_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (clicked_index != -1) {
|
||||
current_shader_index = clicked_index;
|
||||
std::cout << "Switched to shader: " << shaders[current_shader_index].name << std::endl;
|
||||
}
|
||||
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_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {}
|
||||
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 (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) {
|
||||
int y_start = BUTTON_MARGIN + i * (BUTTON_HEIGHT + BUTTON_MARGIN);
|
||||
int y_end = y_start + BUTTON_HEIGHT;
|
||||
if (cursor_y >= y_start && cursor_y <= y_end && cursor_x >= BUTTON_MARGIN && cursor_x <= SIDEBAR_WIDTH - BUTTON_MARGIN) {
|
||||
clicked_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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 const struct wl_pointer_listener pointer_listener = {
|
||||
.enter = pointer_handle_enter,
|
||||
@@ -371,12 +488,19 @@ 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,12 +508,79 @@ 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);
|
||||
|
||||
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);
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user