commit 04a707b4ed914f155bcf1e9d4ab77e685b262961 Author: Florian Stecker Date: Sun Dec 7 19:04:34 2025 -0500 initial version 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); +}