From 04a707b4ed914f155bcf1e9d4ab77e685b262961 Mon Sep 17 00:00:00 2001 From: Florian Stecker Date: Sun, 7 Dec 2025 19:04:34 -0500 Subject: [PATCH] initial version --- Makefile | 15 + billiards.c | 498 ++++++++++++++++++++++++++++++++ cube.h | 37 +++ fragment.glsl | 20 ++ fragment3d.glsl | 12 + gl3.c | 176 +++++++++++ gl3.h | 34 +++ sphere.h | 81 ++++++ tables/dreieck6-dicht | 4 + tables/dreieck6-periodisch | 4 + tables/dreieck6-senkrecht | 4 + tables/dreieck8-periodisch | 4 + tables/hantel | 13 + tables/l-dicht | 7 + tables/l-periodisch | 7 + tables/nichtkonvex | 7 + tables/nichtkonvex-2 | 7 + tables/orthogonal | 4 + tables/rechteck-dicht | 5 + tables/rechteck-horizontal | 5 + tables/rechteck-periodisch-kurz | 5 + tables/rechteck-periodisch-lang | 5 + tables/sanduhr | 7 + tables/stern | 9 + tables/stern-rational | 10 + tables/test | 7 + tables/zahn | 6 + vertex.glsl | 14 + vertex3d.glsl | 14 + 29 files changed, 1021 insertions(+) create mode 100644 Makefile create mode 100644 billiards.c create mode 100644 cube.h create mode 100644 fragment.glsl create mode 100644 fragment3d.glsl create mode 100644 gl3.c create mode 100644 gl3.h create mode 100644 sphere.h create mode 100644 tables/dreieck6-dicht create mode 100644 tables/dreieck6-periodisch create mode 100644 tables/dreieck6-senkrecht create mode 100644 tables/dreieck8-periodisch create mode 100644 tables/hantel create mode 100644 tables/l-dicht create mode 100644 tables/l-periodisch create mode 100644 tables/nichtkonvex create mode 100644 tables/nichtkonvex-2 create mode 100644 tables/orthogonal create mode 100644 tables/rechteck-dicht create mode 100644 tables/rechteck-horizontal create mode 100644 tables/rechteck-periodisch-kurz create mode 100644 tables/rechteck-periodisch-lang create mode 100644 tables/sanduhr create mode 100644 tables/stern create mode 100644 tables/stern-rational create mode 100644 tables/test create mode 100644 tables/zahn create mode 100644 vertex.glsl create mode 100644 vertex3d.glsl diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9131a3a --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +OPTS=-O0 -g + +all: billiards + +gl3.o: gl3.c gl3.h + g++ -c $(OPTS) gl3.c + +billiards.o: billiards.c gl3.h sphere.h + g++ -c $(OPTS) billiards.c + +billiards: billiards.o gl3.o + g++ $(OPTS) -o billiards billiards.o gl3.o -lX11 -lGL -lGLEW + +clean: + rm billiards billiards.o gl3.o diff --git a/billiards.c b/billiards.c new file mode 100644 index 0000000..599e3d7 --- /dev/null +++ b/billiards.c @@ -0,0 +1,498 @@ +#include +#include +#define GLM_ENABLE_EXPERIMENTAL 1 +#include +#include + +#include "gl3.h" +#include "sphere.h" + +#define EPSILON 1e-6 +#define PATH_VERTICES 10000 +#define ARROW_VERTICES 6 + +// functions +static void start_timer(); +static void wait_update_timer(); +static bool checkEvents(); +static int findCollision(double x1, double x2, double v1, double v2, float *line_strip, int nVertices, double *time, double *n1, double *n2); +static int calculatePath(GLfloat *outline, int nOutlineVertices, GLfloat *path, GLfloat *times, int nPathVertices, double x1, double x2, double v1, double v2); +static bool processEvent(XEvent *ev); +static void select_outline(int i); +static void init(); +static void cleanup(); +static void draw(); + +// globals +struct timeval start_time; +unsigned long frames; +double elapsed, frametime; + +GLInfo gl; +GLuint program, program3d; +unsigned int width = 100, height = 100; +float speed = 1, flowtime = 0; +int stopped = 0; +int flowtime_index = 0; + +FILE *dumpfile; + +GLuint outlineVA, outlineVB; +GLuint pathVA, pathVB[2]; +GLuint arrowVA, arrowVB; +GLuint ballVA; + +int outline_index = 0; +int outline_count = 12; +int outline_lengths[] = {5, 5, 5, 5, 4, 4, 4, 4, 7, 7, 7, 13}; +GLfloat outlines[12][30] = { + {-2, -1, 2, -1, 2, 1, -2, 1, -2, -1}, + {-2, -1, 2, -1, 2, 1, -2, 1, -2, -1}, + {-2, -1, 2, -1, 2, 1, -2, 1, -2, -1}, + {-2, -1, 2, -1, 2, 1, -2, 1, -2, -1}, + {-2, -1, 2, -1, 2, 4/sqrt(3) - 1, -2, -1}, + {-2, -1, 2, -1, 2, 4/sqrt(3) - 1, -2, -1}, + {-2, -1, 2, -1, 2, 4/sqrt(3) - 1, -2, -1}, + {-2, -1, 2, -1, 2, 4*tan(M_PI/8) - 1, -2, -1}, + {1, 1, 1, 0, 2.7614348, 0, 2.7614348, -1, -1, -1, -1, 1, 1, 1}, + {1, 1, 1, 0, 2.7614348, 0, 2.7614348, -1, -1, -1, -1, 1, 1, 1}, + {-1.2, 1, 1.2, 1, 0.2, 0, 1.2, -1, -1.2, -1, -0.2, 0, -1.2, 1}, + {-2, -1, -1, -1, -1, -0.05, 1, -0.05, 1, -1, 2, -1, 2, 1, 1, 1, 1, 0.05, -1, 0.05, -1, 1, -2, 1, -2, -1}, +}; +GLfloat starting_vectors[] = { + -1, 0, 2, 0, + -1, 0, 1.6, 2.4, + -1, 0, 7.04, 8, + -1, 0, 10, 7.34819206, + 0, -0.5, 1, -sqrt(3), + 0, -0.5, 1, 2.5*sqrt(3), + 0, -0.5, 5, 5, + 0.4, -0.7, -0.5, 1 + sqrt(2), + -0.2, -0.5, 0.5, -0.5, + 0.5, 0.7, 1, -1, + -0.2, -0.1, 2, 1, + -1.5, -0.1, 1, 2*sqrt(2), +}; + +char filename[1000]; +int outline_length; +GLfloat outline[1000]; +GLfloat starting_vector[4]; + +int nPathVertices; +GLfloat path[PATH_VERTICES*2]; +GLfloat times[PATH_VERTICES]; +GLfloat arrow[ARROW_VERTICES*2] = {0, 0, 1, 0, 0.9, 0.1, 1, 0, 0.9, -0.1, 1, 0}; + +static void start_timer() +{ + gettimeofday(&start_time, NULL); + frames = 0; +} + +static void wait_update_timer() +{ + struct timeval current_time; + double new_elapsed; + + gettimeofday(¤t_time, NULL); + new_elapsed = current_time.tv_sec - start_time.tv_sec; + new_elapsed += (current_time.tv_usec - start_time.tv_usec) * 1e-6; + frametime = new_elapsed - elapsed; + if(frametime < 0.01) { // frames < 10ms are considered too short; sleep a while and then measure again + usleep(10000 - frametime*1e6); + gettimeofday(¤t_time, NULL); + new_elapsed = current_time.tv_sec - start_time.tv_sec; + new_elapsed += (current_time.tv_usec - start_time.tv_usec) * 1e-6; + frametime = new_elapsed - elapsed; + } + elapsed = new_elapsed; + frames++; +} + +static bool checkEvents() // get any events from the queue and the server, process them if neccessary, quit if wanted +{ + XEvent ev; + + while(XCheckIfEvent(gl.display, &ev, alwaysTruePredicate, NULL)) { // we essentially want XCheckWindowEvent, but we want to avoid that events for other windows fill up the queue + if(ev.xany.window != gl.win) + continue; + + if(ev.type == KeyRelease) { // deal with autorepeat + XEvent nev; + if(XCheckIfEvent(gl.display, &nev, alwaysTruePredicate, NULL)) { // is there another event? + if (nev.type == KeyPress && nev.xkey.time == ev.xkey.time && nev.xkey.keycode == ev.xkey.keycode) // which is equal, but KeyPress? Then it's just auto-repeat + continue; // so we ignore both + + XPutBackEvent(gl.display, &nev); // otherwise put the event back, we will consider it in the next round + } + } + + if(processEvent(&ev)) + return true; // quit event queue and application + } + + return false; +} + +/********************************** INTERESTING PART STARTS HERE ***************************************************/ + +static int findCollision(double x1, double x2, double v1, double v2, float *line_strip, int nVertices, double *time, double *n1, double *n2) +{ + double a1, a2, w1, w2, t, s; + int near_wall_count = 0; + int index = -1; + + *time = INFINITY; + + for(int i = 0; i < nVertices - 1; i++) { + a1 = line_strip[2*i]; + a2 = line_strip[2*i+1]; + w1 = line_strip[2*i+2] - a1; + w2 = line_strip[2*i+3] - a2; + // x + tv = a + sw + s = ((x1-a1)*v2 - (x2-a2)*v1) / (w1*v2 - w2*v1); + t = ((x1-a1)*w2 - (x2-a2)*w1) / (w1*v2 - w2*v1); + if(s <= 1 && s >= 0) { + if(t < EPSILON && t > -EPSILON) + near_wall_count ++; + else if(t > 0 && t < *time) { + *time = t; + if((x1-a1)*w2 - (x2-a2)*w1 >= 0) { + *n1 = w2; + *n2 = -w1; + } else { + *n1 = -w2; + *n2 = w1; + } + index = i; + } + } + } + + if(index == -1 || near_wall_count > 1) { + return 0; // failed + } + else + return 1; +} + +static int calculatePath(GLfloat *outline, int nOutlineVertices, GLfloat *path, GLfloat *times, int nPathVertices, double x1, double x2, double v1, double v2) +{ + double t, n1, n2, v1new, v2new, ttotal; + + memset(path, 0, nPathVertices*2*sizeof(GLfloat)); + path[0] = x1; + path[1] = x2; + times[0] = ttotal = 0; + for(int i = 1; i < nPathVertices; i++) { + if(findCollision(x1, x2, v1, v2, outline, nOutlineVertices, &t, &n1, &n2) == 0) { // we hit a singularity, so just stay here and set the next time to infinity + path[2*i] = x1; + path[2*i+1] = x2; + times[i] = INFINITY; + return i + 1; + } + x1 += t * v1; + x2 += t * v2; + v1new = v1 - 2 * (v1*n1 + v2*n2) * n1 / (n1*n1 + n2*n2); // reflect v along the normal to n + v2new = v2 - 2 * (v1*n1 + v2*n2) * n2 / (n1*n1 + n2*n2); + v1 = v1new; + v2 = v2new; + ttotal += t; + path[2*i] = x1; + path[2*i+1] = x2; + times[i] = ttotal; + } + + return nPathVertices; +} + +static bool processEvent(XEvent *ev) +{ + int state; + + switch(ev->type) { + + case ConfigureNotify: + // printf("ConfigureNotify Event, new dimensions: %d %d %d %d\n", ev->xconfigure.x, ev->xconfigure.y, ev->xconfigure.width, ev->xconfigure.height); + width = ev->xconfigure.width; + height = ev->xconfigure.height; + glViewport(0, 0, width, height); + break; + + case KeyPress: + state = ev->xkey.state & (ShiftMask | LockMask | ControlMask); + // printf("KeyPress Event, keycode: %d, state: %d, masked state: %d\n", ev->xkey.keycode, ev->xkey.state, state); + if(state == 0 && ev->xkey.keycode == 26) { + printf("Quit\n"); + return true; + } else if(state == 0 && ev->xkey.keycode == 27) { + select_outline(0); + } else if (state == 0 && ev->xkey.keycode == 114) { + speed *= 2; + } else if (state == 0 && ev->xkey.keycode == 113) { + speed *= 0.5; + } else if (state == 0 && ev->xkey.keycode == 65) { + stopped = !stopped; + } else if (state == 0 && ev->xkey.keycode >= 10 && ev->xkey.keycode <= 19) { + if(ev->xkey.keycode - 10 < outline_count) + select_outline(ev->xkey.keycode - 10); + } + break; + + case KeyRelease: + state = ev->xkey.state & (ShiftMask | LockMask | ControlMask); + // printf("KeyRelease Event, keycode: %d, state: %d, masked state: %d\n", ev->xkey.keycode, ev->xkey.state, state); + break; + + case ClientMessage: + if((Atom)ev->xclient.message_type == gl.wm_protocols && (Atom)ev->xclient.data.l[0] == gl.wm_delete_window) { + printf("Window closed\n"); + return true; + } + break; + + default: + // printf("Event of type %d\n", ev->type); + break; + } + + return false; +} + +static int load_outline(const char *filename) +{ + int c; + FILE *f = fopen(filename, "r"); + if(!f) + return 0; + + double x, y, vx, vy, x0, y0; + + printf("Loading file %s:\n", filename); + + if(fscanf(f, "%lf %lf %lf %lf", &x, &y, &vx, &vy) != 4) + return 0; + + printf("Line: %lf %lf %lf %lf\n", x, y, vx, vy); + starting_vector[0] = x; + starting_vector[1] = y; + starting_vector[2] = vx; + starting_vector[3] = vy; + + do { c = fgetc(f); } while(c != EOF && c != '\n'); + fscanf(f, "%lf %lf", &x0, &y0); + printf("Line: %lf %lf\n", x0, y0); + outline[0] = x0; + outline[1] = y0; + outline_length = 1; + + while(!feof(f)) { + if(fscanf(f, "%lf %lf", &x, &y) != 2) + continue; + + do { c = fgetc(f); } while(c != EOF && c != '\n'); + printf("Line: %lf %lf\n", x, y); + outline[outline_length*2] = x; + outline[outline_length*2+1] = y; + outline_length++; + } + + outline[outline_length*2] = x0; + outline[outline_length*2+1] = y0; + outline_length++; + + fclose(f); + + return 1; +} + +static void select_outline(int i) +{ + load_outline(filename); + nPathVertices = calculatePath(outline, outline_length, path, times, PATH_VERTICES, starting_vector[0], starting_vector[1], starting_vector[2], starting_vector[3]); + + //nPathVertices = calculatePath(outlines[i], outline_lengths[i], path, times, PATH_VERTICES, starting_vectors[4*i], starting_vectors[4*i+1], starting_vectors[4*i+2], starting_vectors[4*i+3]); + + printf("Calculated path until time: %.2f\n", times[nPathVertices - 1]); + + glBindBuffer(GL_ARRAY_BUFFER, outlineVB); + // glBufferData(GL_ARRAY_BUFFER, outline_lengths[i]*2*sizeof(GLfloat), outlines[i], GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, outline_length*2*sizeof(GLfloat), outline, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, pathVB[0]); + glBufferData(GL_ARRAY_BUFFER, nPathVertices*2*sizeof(GLfloat), path, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, pathVB[1]); + glBufferData(GL_ARRAY_BUFFER, nPathVertices*sizeof(GLfloat), times, GL_STATIC_DRAW); + + // outline_index = i; + flowtime = 0; + flowtime_index = 0; + stopped = 1; + speed = 1; +} + +static void init() +{ + glGenVertexArrays(1, &outlineVA); + glBindVertexArray(outlineVA); + glGenBuffers(1, &outlineVB); + glBindBuffer(GL_ARRAY_BUFFER, outlineVB); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glGenVertexArrays(1, &arrowVA); + glBindVertexArray(arrowVA); + glGenBuffers(1, &arrowVB); + glBindBuffer(GL_ARRAY_BUFFER, arrowVB); + glBufferData(GL_ARRAY_BUFFER, ARROW_VERTICES*2*sizeof(GLfloat), arrow, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glGenVertexArrays(1, &pathVA); + glBindVertexArray(pathVA); + glGenBuffers(2, pathVB); + glBindBuffer(GL_ARRAY_BUFFER, pathVB[0]); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, pathVB[1]); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, 0); + + select_outline(outline_index); + + // ball + generateSphere(20, 20, &ballVA); + + initShaders("vertex.glsl", "fragment.glsl", &program); + initShaders("vertex3d.glsl", "fragment3d.glsl", &program3d); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_CULL_FACE); + glFrontFace(GL_CW); +} + +static void cleanup() +{ + // fclose(dumpfile); +} + +static void draw() +{ + glClearColor(1, 1, 1, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if(!stopped) + flowtime += frametime*speed; + + glm::mat4 proj = glm::perspective((float)M_PI/4, (float)width / (float)height, 0.1f, 100.0f); + glm::mat4 view = glm::lookAt(glm::vec3(0,0,7), glm::vec3(0,0,0), glm::vec3(0,1,0)); + + glUseProgram(program); + + // draw arrow + glm::mat4 rotation = glm::mat4(1.0); + // GLfloat x = starting_vectors[4*outline_index+2]; + // GLfloat y = starting_vectors[4*outline_index+3]; + GLfloat x = starting_vector[2]; + GLfloat y = starting_vector[3]; + GLfloat norm = sqrt(x*x + y*y); + x *= 0.5/norm; + y *= 0.5/norm; + rotation[0][0] = x; + rotation[0][1] = y; + rotation[1][0] = -y; + rotation[1][1] = x; + // glm::mat4 model = glm::translate(glm::vec3(starting_vectors[4*outline_index], starting_vectors[4*outline_index+1], 0)) * rotation; + glm::mat4 model = glm::translate(glm::vec3(starting_vector[0], starting_vector[1], 0)) * rotation; + glm::mat4 mvp = proj * view * model; + + glUniformMatrix4fv(glGetUniformLocation(program, "mvp"), 1, GL_FALSE, &mvp[0][0]); + glUniform1i(glGetUniformLocation(program, "type"), 2); + glBindVertexArray(arrowVA); + glDrawArrays(GL_LINES, 0, ARROW_VERTICES); + + // draw outline and path + mvp = proj * view; + + glUniformMatrix4fv(glGetUniformLocation(program, "mvp"), 1, GL_FALSE, &mvp[0][0]); + glUniform1i(glGetUniformLocation(program, "type"), 1); + glBindVertexArray(outlineVA); + // glDrawArrays(GL_LINE_STRIP, 0, outline_lengths[outline_index]); + glDrawArrays(GL_LINE_STRIP, 0, outline_length); + + glUniform1i(glGetUniformLocation(program, "type"), 0); + glUniform1f(glGetUniformLocation(program, "time"), flowtime); + glBindVertexArray(pathVA); + glDrawArrays(GL_LINE_STRIP, 0, nPathVertices); + + // calculate ball position from flowtime (and flowtime_index), times and path + GLfloat pos[3]; + while(flowtime_index < nPathVertices - 1 && times[flowtime_index+1] < flowtime) + flowtime_index++; + if(flowtime_index < nPathVertices - 1) { + pos[0] = path[2*flowtime_index + 0] + (path[2*flowtime_index + 2] - path[2*flowtime_index + 0])*(flowtime - times[flowtime_index])/(times[flowtime_index+1] - times[flowtime_index]); + pos[1] = path[2*flowtime_index + 1] + (path[2*flowtime_index + 3] - path[2*flowtime_index + 1])*(flowtime - times[flowtime_index])/(times[flowtime_index+1] - times[flowtime_index]); + pos[2] = 0; + } else { + pos[0] = path[2*flowtime_index + 0]; + pos[1] = path[2*flowtime_index + 1]; + pos[2] = 0; + } + + // draw ball + glm::mat4 mv = view * glm::translate(glm::vec3(pos[0], pos[1], pos[2])) * glm::scale(glm::vec3(0.05f, 0.05f, 0.05f)); + mvp = proj * mv; + glm::mat3 normal_transform = glm::transpose(glm::inverse(glm::mat3(mv))); + + glUseProgram(program3d); + glUniformMatrix4fv(glGetUniformLocation(program3d, "mvp"), 1, GL_FALSE, &mvp[0][0]); + glUniformMatrix3fv(glGetUniformLocation(program3d, "normal_transform"), 1, GL_FALSE, &normal_transform[0][0]); + glBindVertexArray(ballVA); + glDrawElements(GL_TRIANGLES, 6*20*20, GL_UNSIGNED_INT, 0); + + // GLuint pixels[width*height]; + // glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels); + // fwrite(pixels, sizeof(GLuint), width*height, dumpfile); + + glXSwapBuffers(gl.display, gl.win); +} + +int main(int argc, char * const *argv) +{ + int screen = 0; + int opt; + + while ((opt = getopt(argc, argv, "s:n:f:")) != -1) { + switch (opt) { + case 's': + screen = atoi(optarg); + break; + case 'n': + outline_index = atoi(optarg) - 1; + break; + case 'f': + strncpy(filename, optarg, 1000); + break; + default: + fprintf(stderr, "Usage: %s [-s screen] [-f file]\n", argv[0]); + return 1; + } + } + + if(!initGL(screen, StructureNotifyMask | KeyPressMask | KeyReleaseMask, &gl)) + return 1; + + init(); + start_timer(); + + while(!checkEvents()) { + draw(); + wait_update_timer(); + } + + cleanup(); + destroyGL(&gl); + + return 0; +} diff --git a/cube.h b/cube.h new file mode 100644 index 0000000..0236bc3 --- /dev/null +++ b/cube.h @@ -0,0 +1,37 @@ +#ifndef CUBE_H +#define CUBE_H + +#include + +static const GLfloat cubeVertices[] = { + -1.0f, -1.0f, -1.0f, + -1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, 1.0f, + 1.0f, 1.0f, -1.0f, + 1.0f, 1.0f, 1.0f, +}; + +static const GLfloat cubeColors[] = { + 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 1.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, +}; + +static const GLuint cubeFaces[] = { + 5, 4, 7, 7, 4, 6, + 1, 5, 3, 3, 5, 7, + 0, 1, 2, 2, 1, 3, + 4, 0, 6, 6, 0, 2, + 7, 6, 3, 3, 6, 2, + 1, 0, 4, 1, 4, 5, +}; + +#endif diff --git a/fragment.glsl b/fragment.glsl new file mode 100644 index 0000000..8c85cc7 --- /dev/null +++ b/fragment.glsl @@ -0,0 +1,20 @@ +#version 130 + +in float normalizedTime; +out vec3 color; + +uniform int type; + +void main() { + if(type == 0) { + if(normalizedTime > 0) + discard; + color = vec3(1, 0, 0); + } else if(type == 1) { + color = vec3(0, 0, 0); + } else if(type == 2) { + color = vec3(0, 0, 1); + } else { + color = vec3(0, 0, 1); + } +} diff --git a/fragment3d.glsl b/fragment3d.glsl new file mode 100644 index 0000000..924cbbd --- /dev/null +++ b/fragment3d.glsl @@ -0,0 +1,12 @@ +#version 130 + +in vec3 normal; +out vec3 color; + +uniform int type; + +void main() { + color = clamp(dot(normal, vec3(-1, 0,0)), 0, 1)*vec3(1, 1, 1) + clamp(dot(normal, vec3(0, 1, 0)), 0, 1)*vec3(1, 1, 1) + 0.1; + // color = vec3(1,1,1); + // color = normal; +} diff --git a/gl3.c b/gl3.c new file mode 100644 index 0000000..5794124 --- /dev/null +++ b/gl3.c @@ -0,0 +1,176 @@ +#include + +#include "gl3.h" + +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, bool, const int*); + +#define ERROR(condition, msg, ...) if(condition){fprintf(stderr, msg, ##__VA_ARGS__); return false;} + +bool initGL(int screen, int mask, GLInfo *glinfo) +{ + glinfo->display = XOpenDisplay(NULL); + ERROR(!glinfo->display, "Failed to open X display\n"); + + // check GLX version >= 1.3 + int glx_major, glx_minor; + ERROR(!glXQueryVersion(glinfo->display, &glx_major, &glx_minor), "Could not query GLX version\n"); + ERROR(glx_major == 1 && glx_minor < 3 || glx_major < 1, "Invalid GLX version\n"); + + // Let GLX suggest a frame buffer config with a minimum feature set + static int visual_attribs[] = { + GLX_X_RENDERABLE , True, + GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, + GLX_RENDER_TYPE , GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, + GLX_RED_SIZE , 8, + GLX_GREEN_SIZE , 8, + GLX_BLUE_SIZE , 8, + GLX_ALPHA_SIZE , 8, + GLX_DEPTH_SIZE , 24, + GLX_STENCIL_SIZE , 8, + GLX_DOUBLEBUFFER , True, + None + }; + int fbcount; + GLXFBConfig *fbclist = glXChooseFBConfig(glinfo->display, screen, visual_attribs, &fbcount); + ERROR(!fbclist, "Failed to retrieve a framebuffer config\n"); + GLXFBConfig fbc = fbclist[0]; + XFree(fbclist); + + // Create X Window + XVisualInfo *vi = glXGetVisualFromFBConfig(glinfo->display, fbc); + ERROR(!vi, "Failed to get visual from framebuffer config\n"); + XSetWindowAttributes swa; + glinfo->cmap = XCreateColormap(glinfo->display, RootWindow(glinfo->display, vi->screen), vi->visual, AllocNone); + swa.colormap = glinfo->cmap; + swa.background_pixmap = None; + swa.border_pixel = 0; + swa.event_mask = mask; + glinfo->win = XCreateWindow(glinfo->display, RootWindow(glinfo->display, vi->screen), 0, 0, 100, 100, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa); + ERROR(!glinfo->win, "Failed to create window.\n"); + XFree(vi); + + // Print GLX Extensions + // const char *glxExts = glXQueryExtensionsString(glinfo->display, screen); + // printf("%s\n", glxExts); + + // Create GLX context + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; + glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddressARB((const GLubyte *) "glXCreateContextAttribsARB"); + ERROR(!glXCreateContextAttribsARB, "GLX_ARB_create_context is not supported\n"); + int context_attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + None + }; + glinfo->ctx = glXCreateContextAttribsARB(glinfo->display, fbc, 0, True, context_attribs); + ERROR(!glXIsDirect(glinfo->display, glinfo->ctx), "GLX context is not direct\n"); + + XMapWindow(glinfo->display, glinfo->win); + glXMakeCurrent(glinfo->display, glinfo->win, glinfo->ctx); + + GLenum result = glewInit(); + ERROR(result != GLEW_OK, "Failed to initialize GLEW: %s\n", glewGetErrorString(result)); + + char name[] = "Billiards"; + XClassHint *classHint = XAllocClassHint(); + classHint->res_name = classHint->res_class = name; + XSetClassHint(glinfo->display, glinfo->win, classHint); + XStoreName(glinfo->display, glinfo->win, name); + XFree(classHint); + + glinfo->wm_delete_window = XInternAtom(glinfo->display, "WM_DELETE_WINDOW", false); + glinfo->wm_protocols = XInternAtom(glinfo->display, "WM_PROTOCOLS", false); + XSetWMProtocols(glinfo->display, glinfo->win, &glinfo->wm_delete_window, 1); + + return true; +} + +void destroyGL(GLInfo *glinfo) +{ + glXMakeCurrent(glinfo->display, None, 0); + glXDestroyContext(glinfo->display, glinfo->ctx); + XDestroyWindow(glinfo->display, glinfo->win); + XFreeColormap(glinfo->display, glinfo->cmap); + XCloseDisplay(glinfo->display); +} + +bool initShaders(const char *vertexShaderPath, const char *fragmentShaderPath, GLuint *program) +{ + GLuint vertexShader, fragmentShader; + GLint result; + int infoLogLength; + char *infoLog; + FILE *file; + struct stat filestat; + char *vertexShaderSrc, *fragmentShaderSrc; + + // load vertex shader + file = fopen(vertexShaderPath, "r"); + ERROR(!file, "Could not open file %s\n", vertexShaderPath); + fstat(fileno(file), &filestat); + ERROR(filestat.st_size > 10000000, "Unreasonably large shader file\n"); + vertexShaderSrc = (char*)malloc(filestat.st_size + 1); + vertexShaderSrc[filestat.st_size] = 0; // just for safety + fread(vertexShaderSrc, 1, filestat.st_size, file); + fclose(file); + + // load framgent shader + file = fopen(fragmentShaderPath, "r"); + ERROR(!file, "Could not open file %s\n", fragmentShaderPath); + fstat(fileno(file), &filestat); + ERROR(filestat.st_size > 10000000, "Unreasonably large shader file\n"); + fragmentShaderSrc = (char*)malloc(filestat.st_size + 1); + fragmentShaderSrc[filestat.st_size] = 0; // just for safety + fread(fragmentShaderSrc, 1, filestat.st_size, file); + fclose(file); + + // compile vertex shader + vertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertexShader, 1, &vertexShaderSrc, NULL); + glCompileShader(vertexShader); + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &result); + glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &infoLogLength); + infoLog = (char*)malloc(infoLogLength+1); + glGetShaderInfoLog(vertexShader, infoLogLength, NULL, infoLog); + ERROR(!result, "Vertex shader compile error: %s\n", infoLog); + free(infoLog); + + // compile fragment shader + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragmentShader, 1, &fragmentShaderSrc, NULL); + glCompileShader(fragmentShader); + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &result); + glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &infoLogLength); + infoLog = (char*)malloc(infoLogLength+1); + glGetShaderInfoLog(fragmentShader, infoLogLength, NULL, infoLog); + ERROR(!result, "Fragment shader compile error: %s\n", infoLog); + free(infoLog); + + // link program + *program = glCreateProgram(); + glAttachShader(*program, vertexShader); + glAttachShader(*program, fragmentShader); + glLinkProgram(*program); + glShaderSource(fragmentShader, 1, &fragmentShaderSrc, NULL); + glCompileShader(fragmentShader); + glGetShaderiv(*program, GL_LINK_STATUS, &result); + glGetShaderiv(*program, GL_INFO_LOG_LENGTH, &infoLogLength); + infoLog = (char*)malloc(infoLogLength+1); + glGetProgramInfoLog(*program, infoLogLength, NULL, infoLog); + ERROR(!result, "Shader linking error: %s\n", infoLog); + free(infoLog); + + glDetachShader(*program, vertexShader); + glDetachShader(*program, fragmentShader); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + free(vertexShaderSrc); + free(fragmentShaderSrc); + + return true; +} diff --git a/gl3.h b/gl3.h new file mode 100644 index 0000000..9483d58 --- /dev/null +++ b/gl3.h @@ -0,0 +1,34 @@ +#ifndef GL3_H +#define GL3_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +typedef struct { + Display *display; + Window win; + Colormap cmap; + GLXContext ctx; + Atom wm_protocols; + Atom wm_delete_window; +} GLInfo; + +bool initGL(int screen, int mask, GLInfo *glinfo); +void destroyGL(GLInfo *glinfo); +bool initShaders(const char *vertexShaderSrc, const char *fragmentShaderSrc, GLuint *program); + +static Bool alwaysTruePredicate(Display *display, XEvent *event, XPointer arg) +{ + return true; +} + +#endif diff --git a/sphere.h b/sphere.h new file mode 100644 index 0000000..10838cb --- /dev/null +++ b/sphere.h @@ -0,0 +1,81 @@ +#ifndef SPHERE_H +#define SPHERE_H + +#include + +void generateSphere(int w, int h, GLuint *VA) +{ + GLuint VB, NB, IB; + GLfloat vertices[3*w*h + 6]; + GLfloat normals[3*w*h + 6]; + GLuint indices[6*w*h]; + + memset(vertices, 0, sizeof(vertices)); + memset(normals, 0, sizeof(normals)); + memset(indices, 0, sizeof(indices)); + + for(int i = 0; i < h; i++) + for(int j = 0; j < w; j++) { + vertices[3*i*w + 3*j + 0] = normals[3*i*w + 3*j + 0] = sin((float)(i+1.0)/(h+1.0)*M_PI) * cos((float)j/w*2*M_PI); + vertices[3*i*w + 3*j + 1] = normals[3*i*w + 3*j + 1] = sin((float)(i+1.0)/(h+1.0)*M_PI) * sin((float)j/w*2*M_PI); + vertices[3*i*w + 3*j + 2] = normals[3*i*w + 3*j + 2] = cos((float)(i+1.0)/(h+1.0)*M_PI); + } + + vertices[3*w*h + 0] = normals[3*w*h + 0] = 0; + vertices[3*w*h + 1] = normals[3*w*h + 1] = 0; + vertices[3*w*h + 2] = normals[3*w*h + 2] = 1; + vertices[3*w*h + 3] = normals[3*w*h + 3] = 0; + vertices[3*w*h + 4] = normals[3*w*h + 4] = 0; + vertices[3*w*h + 5] = normals[3*w*h + 5] = -1; + + for(int i = 0; i < h - 1; i++) { + for(int j = 0; j < w - 1; j++) { + indices[6*i*w + 6*j] = i*w + j; + indices[6*i*w + 6*j + 1] = i*w + j + 1; + indices[6*i*w + 6*j + 2] = i*w + j + w; + indices[6*i*w + 6*j + 3] = i*w + j + 1; + indices[6*i*w + 6*j + 4] = i*w + j + w + 1; + indices[6*i*w + 6*j + 5] = i*w + j + w; + } + indices[6*i*w + 6*w - 6] = i*w + w - 1; + indices[6*i*w + 6*w - 5] = i*w; + indices[6*i*w + 6*w - 4] = i*w + 2*w - 1; + indices[6*i*w + 6*w - 3] = i*w; + indices[6*i*w + 6*w - 2] = i*w + w; + indices[6*i*w + 6*w - 1] = i*w + 2*w - 1; + } + + for(int i = 0; i < w - 1; i++) { + indices[6*h*w - 6*w + 6*i + 0] = i; + indices[6*h*w - 6*w + 6*i + 1] = w*h; + indices[6*h*w - 6*w + 6*i + 2] = i+1; + indices[6*h*w - 6*w + 6*i + 3] = w*h - w + i + 1; + indices[6*h*w - 6*w + 6*i + 4] = w*h + 1; + indices[6*h*w - 6*w + 6*i + 5] = w*h - w + i; + } + + indices[6*h*w - 6 + 0] = w - 1; + indices[6*h*w - 6 + 1] = w*h; + indices[6*h*w - 6 + 2] = 0; + + glGenVertexArrays(1, VA); + glBindVertexArray(*VA); + + glGenBuffers(1, &VB); + glBindBuffer(GL_ARRAY_BUFFER, VB); + glBufferData(GL_ARRAY_BUFFER, (3*w*h+6)*sizeof(GLfloat), vertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); + + glGenBuffers(1, &NB); + glBindBuffer(GL_ARRAY_BUFFER, NB); + glBufferData(GL_ARRAY_BUFFER, (3*w*h+6)*sizeof(GLfloat), normals, GL_STATIC_DRAW); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0); + + glGenBuffers(1, &IB); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IB); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6*w*h*sizeof(GLuint), indices, GL_STATIC_DRAW); +} + +#endif diff --git a/tables/dreieck6-dicht b/tables/dreieck6-dicht new file mode 100644 index 0000000..5b8b22a --- /dev/null +++ b/tables/dreieck6-dicht @@ -0,0 +1,4 @@ +0 -0.5 5 5 +-2 -1 +2 -1 +2 1.309401076758 diff --git a/tables/dreieck6-periodisch b/tables/dreieck6-periodisch new file mode 100644 index 0000000..855ef0e --- /dev/null +++ b/tables/dreieck6-periodisch @@ -0,0 +1,4 @@ +0 -0.5 1 4.33012701892 +-2 -1 +2 -1 +2 1.309401076758 diff --git a/tables/dreieck6-senkrecht b/tables/dreieck6-senkrecht new file mode 100644 index 0000000..ec596ff --- /dev/null +++ b/tables/dreieck6-senkrecht @@ -0,0 +1,4 @@ +0.3 -0.3 0 -1.000000 +-2 -1 +2 -1 +2 1.309401076758 diff --git a/tables/dreieck8-periodisch b/tables/dreieck8-periodisch new file mode 100644 index 0000000..3758ec8 --- /dev/null +++ b/tables/dreieck8-periodisch @@ -0,0 +1,4 @@ +0.4 -0.7 -0.5 2.41421356237 +-2 -1 +2 -1 +2 0.656854249492 diff --git a/tables/hantel b/tables/hantel new file mode 100644 index 0000000..42463b3 --- /dev/null +++ b/tables/hantel @@ -0,0 +1,13 @@ +-1.5 -0.1 1 2.12345567899 +-2 -1 +-1 -1 +-1 -0.05 +1 -0.05 +1 -1 +2 -1 +2 1 +1 1 +1 0.05 +-1 0.05 +-1 1 +-2 1 diff --git a/tables/l-dicht b/tables/l-dicht new file mode 100644 index 0000000..e5e3344 --- /dev/null +++ b/tables/l-dicht @@ -0,0 +1,7 @@ +0.2 0.5 0.5 -0.5 # Kommentar +1 1 +1 0 +2.276394569 0 +2.276394569 -1 +-1 -1 +-1 1 diff --git a/tables/l-periodisch b/tables/l-periodisch new file mode 100644 index 0000000..a0e3289 --- /dev/null +++ b/tables/l-periodisch @@ -0,0 +1,7 @@ +0.7 0.5 0.5 -0.5 # Kommentar +1 1 +1 0 +2.276394569 0 +2.276394569 -1 +-1 -1 +-1 1 diff --git a/tables/nichtkonvex b/tables/nichtkonvex new file mode 100644 index 0000000..08d09a1 --- /dev/null +++ b/tables/nichtkonvex @@ -0,0 +1,7 @@ +0.5 0 0 -1.5 +0 -1 +-0.5 1.8356409098088555 +0.5 0.6438873172146455 +1 1.0634371328032852 +1.5 0.7133333636984291 +0.5502838083459437 -1 diff --git a/tables/nichtkonvex-2 b/tables/nichtkonvex-2 new file mode 100644 index 0000000..90c1939 --- /dev/null +++ b/tables/nichtkonvex-2 @@ -0,0 +1,7 @@ +0.3 0 0 -1.5 +0 -1 +-0.5 1.8356409098088555 +0.5 0.6438873172146455 +1 1.0634371328032852 +1.5 0.7133333636984291 +0.5502838083459437 -1 diff --git a/tables/orthogonal b/tables/orthogonal new file mode 100644 index 0000000..5b858ca --- /dev/null +++ b/tables/orthogonal @@ -0,0 +1,4 @@ +1 0 0 -1 +-2 -1 +2 -1 +0.33512798812026023 1.8836429126751244 # Dreieck mit (51,60,69)-Grad Winkel diff --git a/tables/rechteck-dicht b/tables/rechteck-dicht new file mode 100644 index 0000000..8feca9b --- /dev/null +++ b/tables/rechteck-dicht @@ -0,0 +1,5 @@ +-1 0 10 7.34819206 +-2 -1 +2 -1 +2 1 +-2 1 diff --git a/tables/rechteck-horizontal b/tables/rechteck-horizontal new file mode 100644 index 0000000..7503f5c --- /dev/null +++ b/tables/rechteck-horizontal @@ -0,0 +1,5 @@ +-1 0 2 0 +-2 -1 +2 -1 +2 1 +-2 1 diff --git a/tables/rechteck-periodisch-kurz b/tables/rechteck-periodisch-kurz new file mode 100644 index 0000000..f2acd87 --- /dev/null +++ b/tables/rechteck-periodisch-kurz @@ -0,0 +1,5 @@ +-1 0 1.6 2.4 +-2 -1 +2 -1 +2 1 +-2 1 diff --git a/tables/rechteck-periodisch-lang b/tables/rechteck-periodisch-lang new file mode 100644 index 0000000..d4bffe6 --- /dev/null +++ b/tables/rechteck-periodisch-lang @@ -0,0 +1,5 @@ +-1 0 7.04 8 +-2 -1 +2 -1 +2 1 +-2 1 diff --git a/tables/sanduhr b/tables/sanduhr new file mode 100644 index 0000000..983ff7c --- /dev/null +++ b/tables/sanduhr @@ -0,0 +1,7 @@ +-0.2 -0.1 2 1 +-1.2 1 +1.2 1 +0.2 0 +1.2 -1 +-1.2 -1 +-0.2 0 diff --git a/tables/stern b/tables/stern new file mode 100644 index 0000000..7386930 --- /dev/null +++ b/tables/stern @@ -0,0 +1,9 @@ +0.2 0.5 0.5 -0.5 +0 1.5 +0.5 0.5 +1.5 0 +0.5 -0.5 +0 -1.5 +-0.5 -0.5 +-1.5 0 +-0.5 0.5 diff --git a/tables/stern-rational b/tables/stern-rational new file mode 100644 index 0000000..67fc5a8 --- /dev/null +++ b/tables/stern-rational @@ -0,0 +1,10 @@ +0.5 0 0.5 -0.8660254037844386 +0 1.3660254037844386 +0.5 0.5 +1.3660254037844386 0 +0.5 -0.5 +0 -1.3660254037844386 +-0.5 -0.5 +-1.3660254037844386 0 +-0.5 0.5 + diff --git a/tables/test b/tables/test new file mode 100644 index 0000000..de8622f --- /dev/null +++ b/tables/test @@ -0,0 +1,7 @@ +0.1 0.5 0 -1.5 +0 -1 +-0.5 1.8356409098088555 +0.5 0.6438873172146455 +1 1.0634371328032852 +1.5 0.7133333636984291 +0.5502838083459437 -1 diff --git a/tables/zahn b/tables/zahn new file mode 100644 index 0000000..56320de --- /dev/null +++ b/tables/zahn @@ -0,0 +1,6 @@ +-0.7 0.3 -1 -1 +-1 -1 +1 -1 +1 1 +0 0 +-1 1 diff --git a/vertex.glsl b/vertex.glsl new file mode 100644 index 0000000..2f02a98 --- /dev/null +++ b/vertex.glsl @@ -0,0 +1,14 @@ +#version 130 + +in vec2 pos; +in float vertexTime; + +out float normalizedTime; + +uniform float time; +uniform mat4 mvp; + +void main() { + gl_Position = mvp * vec4(pos.x, pos.y, 0, 1); + normalizedTime = vertexTime - time; +} diff --git a/vertex3d.glsl b/vertex3d.glsl new file mode 100644 index 0000000..59fe0c7 --- /dev/null +++ b/vertex3d.glsl @@ -0,0 +1,14 @@ +#version 130 + +in vec3 pos; +in vec3 vertexNormal; + +out vec3 normal; + +uniform mat4 mvp; +uniform mat3 normal_transform; + +void main() { + gl_Position = mvp * vec4(pos, 1); + normal = normalize(normal_transform * vertexNormal); +}