]> git.lizzy.rs Git - libscout.git/blob - example.c
Done
[libscout.git] / example.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stddef.h>
4 #include <stdbool.h>
5 #include <math.h>
6 #include <GL/glew.h>
7 #include <GL/gl.h>
8 #include <GLFW/glfw3.h>
9 #include "scout.h"
10
11 #define error(...) {fprintf(stderr, __VA_ARGS__); abort();}
12
13 #define UNUSED(x) (void)(x)
14
15 #define VERTSHDSRC_COMMON "" \
16         "uniform float screenRatio;" \
17         "vec2 nodePosNDC(vec2 p)" \
18         "{" \
19         "       return (p * 2 - vec2(1.0)) * vec2(1.0, -1.0);" \
20         "}" \
21         "vec2 scalePercent(vec2 p, float perc)" \
22         "{" \
23         "       return p * vec2(1.0, screenRatio) / 100 * perc;" \
24         "}"
25
26 #define VERTSHDSRC_NODES "#version 330 core\n" \
27         "layout(location = 0) in vec2 vertexCoord;" \
28         "uniform vec2 nodePos;" \
29         "uniform bool nodeSelected;" \
30         VERTSHDSRC_COMMON \
31         "void main()" \
32         "{" \
33         "       vec2 pos = nodePosNDC(nodePos) + scalePercent(vertexCoord, nodeSelected ? 7.5 : 5.0);" \
34         "       gl_Position = vec4(pos, 0.0, 1.0);" \
35         "}"
36
37
38 #define FRAGSHDSRC_NODES "#version 330 core\n" \
39         "uniform bool nodeSelected;" \
40         "void main()" \
41         "{" \
42         "       gl_FragColor = vec4(nodeSelected ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0), 1.0);" \
43         "}"
44         
45 #define VERTSHDSRC_WAYS "#version 330 core\n" \
46         "layout(location = 0) in vec2 vertexCoord;" \
47         "out float progress;" \
48         "uniform vec2 wayPos;" \
49         "uniform float wayLength;" \
50         "uniform vec2 way;" \
51         VERTSHDSRC_COMMON \
52         "void main()" \
53         "{" \
54         "       progress = (vertexCoord.x + 0.5) * wayLength;" \
55         "       vec2 w = normalize(way);" \
56         "       vec2 pos = nodePosNDC(wayPos) + mat2(w.x, -w.y, w.y, w.x) * ((vertexCoord * vec2(wayLength * 2, 0.0125)) * vec2(1.0, screenRatio));" \
57         "       gl_Position = vec4(pos, 0.0, 1.0);" \
58         "}"
59         
60 #define FRAGSHDSRC_WAYS "#version 330 core\n" \
61         "in float progress;" \
62         "uniform float wayProgress;" \
63         "uniform float wayLength;" \
64         "void main()" \
65         "{" \
66         "       vec3 color = vec3(1.0, 0.0, 0.0);" \
67         "       if (wayProgress > 0.0 && wayProgress >= progress || wayProgress < 0.0 && (wayLength + wayProgress) <= progress)" \
68         "               color = vec3(0.0, 0.0, 1.0);" \
69         "       gl_FragColor = vec4(color, 1.0);" \
70         "}"
71
72 struct {
73         float width, height;
74 } screenBounds, screenBoundsNormalized;
75
76 struct {
77         float x, y;
78 } cursorPos;
79
80 struct Node {
81         float x, y;
82         scnode *scnod;
83         bool selected;
84         struct Node *next;
85 };
86
87 struct Way {
88         float posx, posy;
89         float vecx, vecy;
90         bool done;
91         bool active;
92         scway *scway_1;
93         scway *scway_2;
94         struct Way *next;
95 };
96
97 typedef struct Node Node;
98 typedef struct Way Way;
99
100 Node *nodelist = NULL;
101 Way *waylist = NULL;
102
103 Node *selected_node = NULL;
104
105 scwaypoint *currentwp = NULL;
106 float currentprog = 0.0;
107 bool unselect = false;
108
109 GLuint screenRatio_nodes_loc, nodePos_loc, nodeSelected_loc, screenRatio_ways_loc, wayPos_loc, wayLength_loc, way_loc, wayProgress_loc;
110 GLuint nodes_shaders, ways_shaders;
111
112 Node *createNode(float x, float y)
113 {
114         Node *node = malloc(sizeof(Node));
115         node->x = x;
116         node->y = y;
117         node->selected = false;
118         node->scnod = scnodalloc(node);
119         node->next = NULL;
120         for (Node *nptr = nodelist; nptr != NULL; nptr = nptr->next) {
121                 if (nptr->next == NULL) {
122                         return nptr->next = node;
123                 }
124         }
125         return nodelist = node;
126 }
127
128 Way *connectNodes(Node *from, Node *to)
129 {
130         if (scisconnected(from->scnod, to->scnod))
131                 return NULL;
132         Way *way = malloc(sizeof(Way));
133         way->posx = (from->x + to->x) / 2;
134         way->posy = (from->y + to->y) / 2;
135         way->vecx = to->x - from->x;
136         way->vecy = to->y - from->y;
137         way->active = false;
138         way->done = false;
139         float len = sqrt(way->vecx * way->vecx + way->vecy * way->vecy);
140         way->scway_1 = scaddway(from->scnod, to->scnod, len, way);
141         way->scway_2 = scaddway(to->scnod, from->scnod, len, way);
142         way->next = NULL;
143         for (Way *wptr = waylist; wptr != NULL; wptr = wptr->next) {
144                 if (wptr->next == NULL) {
145                         return wptr->next = way;
146                 }
147         }
148         return waylist = way;
149 }
150
151 Node *getNodeAtPos(float x, float y)
152 {
153         Node *node = NULL;
154         float distance;
155         for (Node *nptr = nodelist; nptr != NULL; nptr = nptr->next) {
156                 float dx, dy;
157                 dx = (nptr->x - x) * screenBoundsNormalized.width;
158                 dy = (nptr->y - y) * screenBoundsNormalized.height;
159                 float d = sqrt(dx * dx + dy * dy);
160                 if (d > (nptr->selected ? 7.5f : 5.0f) / 100.0f / 4.0f)
161                         continue;
162                 if (node == NULL || d < distance) {
163                         node = nptr;
164                         distance = d;
165                 }
166         }
167         return node;
168 }
169
170 Way *getActiveWay()
171 {
172         return currentwp != NULL && currentwp->way != NULL ? (Way *)currentwp->way->dat : NULL;
173 }
174
175 Node *getActiveNode()
176 {
177         return currentwp != NULL ? (Node *)currentwp->nod->dat : NULL;
178 }
179
180 void findPathEnd()
181 {
182         unselect = true;
183         scdestroypath(currentwp);
184         currentwp = NULL;
185 }
186
187 void findPathStep(double dtime)
188 {
189         if (currentwp == NULL)
190                 return;
191         Node *active_node = getActiveNode();
192         active_node->selected = true;
193         Way *active_way = getActiveWay();
194         if (! active_way) {
195                 findPathEnd();
196                 return;
197         }
198         Node *to = (Node *)active_way->scway_1->lto->dat;
199         float len = active_way->scway_1->len;
200         float fac = 1.0;
201         if (active_node == to)
202                 fac = -1.0;
203         currentprog += dtime * fac * 0.25;
204         if (currentprog * fac > len) {
205                 active_way->done = true;
206                 currentwp = currentwp->nxt;                     
207                 currentprog = 0.0;
208         }
209 }
210
211 void findPathStart(Node *from, Node *to)
212 {
213         currentwp = scout(from->scnod, to->scnod, NULL);
214         if (currentwp == NULL)
215                 findPathEnd();
216         currentprog = 0.0;
217 }
218
219 GLuint createShaderProgram(const char *vsrc, const char *fsrc)
220 {
221         GLuint id, vsh, fsh;
222         int success;
223         char buffer[1024] = {0};
224         
225         id = glCreateProgram();
226         
227         {
228                 vsh = glCreateShader(GL_VERTEX_SHADER);
229                 glShaderSource(vsh, 1, &vsrc, NULL);
230                 glCompileShader(vsh);
231                 glGetShaderiv(vsh, GL_COMPILE_STATUS, &success);
232                 if (! success) {
233                         glGetShaderInfoLog(vsh, 1024, NULL, buffer);
234                         error("Failed to compile vertex shader: %s\n", buffer);
235                 }
236                 glAttachShader(id, vsh);
237         }
238         
239         {
240                 fsh = glCreateShader(GL_FRAGMENT_SHADER);
241                 glShaderSource(fsh, 1, &fsrc, NULL);
242                 glCompileShader(fsh);
243                 glGetShaderiv(fsh, GL_COMPILE_STATUS, &success);
244                 if (! success) {
245                         glGetShaderInfoLog(fsh, 1024, NULL, buffer);
246                         error("Failed to compile fragment shader: %s\n", buffer);
247                 }
248                 glAttachShader(id, fsh);
249         }
250         
251         glLinkProgram(id);
252         glGetProgramiv(id, GL_LINK_STATUS, &success);
253         if (! success) {
254                 glGetProgramInfoLog(id, 1024, NULL, buffer);
255                 error("Failed to link shader program: %s\n", buffer);
256         }
257         
258         glDeleteShader(vsh);
259         glDeleteShader(fsh);
260         
261         return id;
262 }
263
264 GLuint makeMesh(const GLfloat *vertices, GLsizei vertices_count, const GLuint *indices, GLsizei indices_count)
265 {
266         GLuint VAO, VBO, EBO;
267         
268         glGenVertexArrays(1, &VAO);
269         glGenBuffers(1, &VBO);
270         glGenBuffers(1, &EBO);
271         
272         glBindVertexArray(VAO); 
273         glBindBuffer(GL_ARRAY_BUFFER, VBO);
274         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
275         
276         glBufferData(GL_ARRAY_BUFFER, vertices_count * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
277         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_count * sizeof(GLuint), indices, GL_STATIC_DRAW);
278         
279         glVertexAttribPointer(0, 2, GL_FLOAT, false, 2 * sizeof(GLfloat), 0);
280         glEnableVertexAttribArray(0);
281         
282         glBindVertexArray(0);
283         glBindBuffer(GL_ARRAY_BUFFER, 0);
284         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
285         
286         return VAO;
287 }
288
289 void framebuffer_size_callback(GLFWwindow *window, int width, int height)
290 {
291         UNUSED(window);
292         glViewport(0, 0, width, height);
293         screenBounds.width = width;
294         screenBounds.height = height;
295         float len = sqrt(width * width + height * height);
296         screenBoundsNormalized.width = (float)width / len;
297         screenBoundsNormalized.height = (float)height / len;
298         glProgramUniform1f(nodes_shaders, screenRatio_nodes_loc, (float)width / (float)height);
299         glProgramUniform1f(ways_shaders, screenRatio_ways_loc, (float)width / (float)height);
300 }
301
302 void mouse_button_callback(GLFWwindow *window, int button, int action, int mods)
303 {
304         UNUSED(window);
305         UNUSED(mods);
306         if (action != GLFW_RELEASE || currentwp != NULL)
307                 return;
308         if (unselect) {
309                 for (Way *wptr = waylist; wptr != NULL; wptr = wptr->next)
310                         wptr->done = false;
311                 for (Node *nptr = nodelist; nptr != NULL; nptr = nptr->next)
312                         nptr->selected = false;
313                 unselect = false;
314         }
315         float x = cursorPos.x / screenBounds.width;
316         float y = cursorPos.y / screenBounds.height;
317         Node *nod = getNodeAtPos(x, y);
318         if (nod) {
319                 if (selected_node) {
320                         switch (button) {
321                         case GLFW_MOUSE_BUTTON_LEFT:
322                                 connectNodes(selected_node, nod);
323                                 break;
324                         case GLFW_MOUSE_BUTTON_RIGHT:
325                                 findPathStart(selected_node, nod);
326                                 break;
327                         }
328                         selected_node->selected = false;
329                         selected_node = NULL;
330                 }
331                 else {
332                         selected_node = nod;
333                         selected_node->selected = true;
334                 }
335         } else {
336                 createNode(x, y);
337         }
338 }
339
340 void cursor_pos_callback(GLFWwindow *window, double x, double y)
341 {
342         UNUSED(window);
343         cursorPos.x = x;
344         cursorPos.y = y;
345 }
346
347 int main()
348 {
349         if (! glfwInit())
350                 error("Failed to initialize GLFW\n");
351         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
352         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
353         glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
354         
355         GLFWwindow *window = glfwCreateWindow(10, 10, "libscout Example", NULL, NULL);
356         if (! window) {
357                 glfwTerminate();
358                 error("Failed to create GLFW window\n");
359         }
360         glfwMakeContextCurrent(window);
361         glfwSetFramebufferSizeCallback(window, &framebuffer_size_callback);
362         glfwSetCursorPosCallback(window, &cursor_pos_callback);
363         glfwSetMouseButtonCallback(window, &mouse_button_callback);
364         glfwSetWindowSize(window, 750, 500);
365         
366         GLenum glew_init_err = glewInit();
367         if (glew_init_err != GLEW_OK)
368                 error("Failed to initalize GLEW\n");
369         
370         float degtorad = M_PI / 180.0f;
371         
372         GLfloat circle_vertices[361][2];
373         GLuint circle_indices[360][3];
374         
375         circle_vertices[360][0] = 0;
376         circle_vertices[360][1] = 0;
377         
378         for (int deg = 0; deg < 360; deg++) {
379                 float rad = (float)deg * degtorad;
380                 circle_vertices[deg][0] = cos(rad) * 0.5;
381                 circle_vertices[deg][1] = sin(rad) * 0.5;
382                 
383                 int nextdeg = deg + 1;
384                 
385                 circle_indices[deg][0] = 360;
386                 circle_indices[deg][1] = deg;
387                 circle_indices[deg][2] = nextdeg < 360 ? nextdeg : 0;
388         }
389         
390         GLsizei circle_vertices_count = 361 * 2;
391         
392         GLsizei circle_indices_count = 360 * 3;
393         
394         GLfloat square_vertices[4][2] = {
395                 {+0.5, +0.5},
396                 {+0.5, -0.5},
397                 {-0.5, -0.5},
398                 {-0.5, +0.5},
399         };
400         
401         GLsizei square_vertices_count = 4 * 2;
402         
403         GLuint square_indices[2][3] = {
404                 {0, 1, 3},
405                 {1, 2, 3}, 
406         };
407         
408         GLsizei square_indices_count = 2 * 3;
409         
410         GLuint nodes_VAO, ways_VAO;
411         
412         nodes_VAO = makeMesh(circle_vertices[0], circle_vertices_count, circle_indices[0], circle_indices_count);
413         ways_VAO = makeMesh(square_vertices[0], square_vertices_count, square_indices[0], square_indices_count);
414         
415         nodes_shaders = createShaderProgram(VERTSHDSRC_NODES, FRAGSHDSRC_NODES);
416         ways_shaders = createShaderProgram(VERTSHDSRC_WAYS, FRAGSHDSRC_WAYS);
417         
418         screenRatio_nodes_loc = glGetUniformLocation(nodes_shaders, "screenRatio");
419         nodePos_loc = glGetUniformLocation(nodes_shaders, "nodePos");
420         nodeSelected_loc = glGetUniformLocation(nodes_shaders, "nodeSelected");
421         
422         screenRatio_ways_loc = glGetUniformLocation(ways_shaders, "screenRatio");
423         wayPos_loc = glGetUniformLocation(ways_shaders, "wayPos");
424         wayLength_loc = glGetUniformLocation(ways_shaders, "wayLength");
425         way_loc = glGetUniformLocation(ways_shaders, "way");
426         wayProgress_loc = glGetUniformLocation(ways_shaders, "wayProgress");
427         
428         double last_time = glfwGetTime();
429         
430         while (! glfwWindowShouldClose(window)) {
431                 double dtime = glfwGetTime() - last_time;
432                 last_time = glfwGetTime();
433                 
434                 findPathStep(dtime);
435
436                 glClearColor(1.0, 1.0, 1.0, 1.0);
437                 glClear(GL_COLOR_BUFFER_BIT); 
438
439                 glUseProgram(ways_shaders);
440                 glBindVertexArray(ways_VAO);
441                 Way *active_way = getActiveWay();
442                 for (Way *way = waylist; way != NULL; way = way->next) {
443                         float progress = 0.0;
444                         float len = way->scway_1->len;
445                         if (way == active_way)
446                                 progress = currentprog;
447                         else if (way->done)
448                                 progress = len;
449                         glUniform1f(wayProgress_loc, progress);
450                         glUniform1f(wayLength_loc, len);
451                         glUniform2f(wayPos_loc, way->posx, way->posy);
452                         glUniform2f(way_loc, way->vecx, way->vecy);
453                         glDrawElements(GL_TRIANGLES, square_indices_count, GL_UNSIGNED_INT, 0);
454                 }
455                 
456                 glUseProgram(nodes_shaders);
457                 glBindVertexArray(nodes_VAO);
458                 for (Node *node = nodelist; node != NULL; node = node->next) {
459                         glUniform2f(nodePos_loc, node->x, node->y);
460                         glUniform1i(nodeSelected_loc, node->selected);
461                         glDrawElements(GL_TRIANGLES, circle_indices_count, GL_UNSIGNED_INT, 0);
462                 }
463                 
464                 glBindVertexArray(0);
465
466                 glfwSwapBuffers(window);
467                 glfwPollEvents();
468         }
469 }