b11f874e23065a60b9766bd7a0a73b1c2e0a24f3
[simavr] / examples / board_reprap / src / reprap_gl.c
1 /*
2         reprap_gl.c
3
4         Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
5
6         This file is part of simavr.
7
8         simavr is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         simavr is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with simavr.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #if __APPLE__
23 #define GL_GLEXT_PROTOTYPES
24 #include <GLUT/glut.h>
25 #include <OpenGL/gl.h>
26 #include <OpenGL/glext.h>
27 #else
28 #define GL_GLEXT_PROTOTYPES
29 #include <GL/gl.h>
30 #include <GL/glut.h>
31 #include <GL/glext.h>
32 #endif
33
34 #include <stdio.h>
35 #include <math.h>
36
37 #include "reprap.h"
38 #include "reprap_gl.h"
39
40 #include "c3.h"
41 #include "c3camera.h"
42 #include "c3driver_context.h"
43 #include "c3stl.h"
44 #include "c3lines.h"
45 #include "c3sphere.h"
46 #include "c3program.h"
47 #include "c3gl.h"
48
49 #include <cairo/cairo.h>
50
51 #define FBO 1
52
53 struct cairo_surface_t;
54
55 int _w = 800, _h = 600;
56
57 c3context_p c3 = NULL;
58 c3context_p hud = NULL;
59
60 c3object_p head = NULL;         // hotend
61 c3texture_p fbo_c3;                     // frame buffer object texture
62 c3program_p fxaa = NULL;        // full screen antialias shader
63
64 int glsl_version = 110;
65
66 extern reprap_t reprap;
67
68 static int dumpError(const char * what)
69 {
70         GLenum e;
71         int count = 0;
72         while ((e = glGetError()) != GL_NO_ERROR) {
73                 printf("%s: %s\n", what, gluErrorString(e));
74                 count++;
75         }
76         return count;
77 }
78
79 #define GLCHECK(_w) {_w; dumpError(#_w);}
80
81 /* Global */
82 GLuint fbo, fbo_texture, rbo_depth;
83 //GLuint vbo_fbo_vertices;
84
85 static void
86 gl_offscreenInit(
87                 int screen_width,
88                 int screen_height)
89 {
90         /* init_resources */
91         /* Create back-buffer, used for post-processing */
92
93         /* Texture */
94         GLCHECK(glActiveTexture(GL_TEXTURE0));
95         glGenTextures(1, &fbo_texture);
96         glBindTexture(GL_TEXTURE_2D, fbo_texture);
97         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
98         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
99         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
100         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
101         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screen_width, screen_height, 0,
102                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
103         glBindTexture(GL_TEXTURE_2D, 0);
104
105         /* Depth buffer */
106         GLCHECK(glGenRenderbuffers(1, &rbo_depth));
107         glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth);
108         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, screen_width,
109                 screen_height);
110         glBindRenderbuffer(GL_RENDERBUFFER, 0);
111
112         /* Framebuffer to link everything together */
113         GLCHECK(glGenFramebuffers(1, &fbo));
114         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
115         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
116                 fbo_texture, 0);
117         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
118                 GL_RENDERBUFFER, rbo_depth);
119
120         GLenum status;
121         if ((status = glCheckFramebufferStatus(GL_FRAMEBUFFER))
122                 != GL_FRAMEBUFFER_COMPLETE) {
123                 fprintf(stderr, "glCheckFramebufferStatus: error %d", (int)status);
124                 return ;
125         }
126 #if 0
127         // Set the list of draw buffers.
128         GLenum DrawBuffers[2] = {GL_COLOR_ATTACHMENT0};
129         glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
130         glBindFramebuffer(GL_FRAMEBUFFER, 0);
131 #endif
132 }
133
134 void
135 gl_offscreenReshape(
136                 int screen_width,
137                 int screen_height)
138 {
139 // Rescale FBO and RBO as well
140         glBindTexture(GL_TEXTURE_2D, fbo_texture);
141         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screen_width, screen_height, 0,
142                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
143         glBindTexture(GL_TEXTURE_2D, 0);
144
145         glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth);
146         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, screen_width,
147                 screen_height);
148         glBindRenderbuffer(GL_RENDERBUFFER, 0);
149 }
150
151 void gl_offscreenFree()
152 {
153         /* free_resources */
154         glDeleteRenderbuffers(1, &rbo_depth);
155         glDeleteTextures(1, &fbo_texture);
156         glDeleteFramebuffers(1, &fbo);
157 }
158
159 static void
160 _gl_reshape_cb(int w, int h)
161 {
162     _w  = w;
163     _h = h;
164
165     glViewport(0, 0, _w, _h);
166     gl_offscreenReshape(_w, _h);
167     glutPostRedisplay();
168 }
169
170 static void
171 _gl_key_cb(
172                 unsigned char key,
173                 int x,
174                 int y)  /* called on key press */
175 {
176         switch (key) {
177                 case 'q':
178                 //      avr_vcd_stop(&vcd_file);
179                         c3context_dispose(c3);
180                         exit(0);
181                         break;
182                 case 'r':
183                         printf("Starting VCD trace; press 's' to stop\n");
184                 //      avr_vcd_start(&vcd_file);
185                         break;
186                 case 's':
187                         printf("Stopping VCD trace\n");
188                 //      avr_vcd_stop(&vcd_file);
189                         break;
190                 case '1':
191                         if (fbo_c3->geometry.mat.program)
192                                 fbo_c3->geometry.mat.program = NULL;
193                         else
194                                 fbo_c3->geometry.mat.program = fxaa;
195                         glutPostRedisplay();
196                         break;
197         }
198 }
199
200 static void
201 _gl_display_cb(void)            /* function called whenever redisplay needed */
202 {
203 #if FBO
204         /*
205          * Draw in FBO object
206          */
207         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
208         // draw (without glutSwapBuffers)
209         dumpError("glBindFramebuffer fbo");
210         glViewport(0, 0, _w, _h);
211
212 #else
213         glBindFramebuffer(GL_FRAMEBUFFER, 0);
214 #endif
215
216         c3context_view_set(c3, 0);
217         c3vec3 headp = c3vec3f(
218                         stepper_get_position_mm(&reprap.step_x),
219                         stepper_get_position_mm(&reprap.step_y),
220                         stepper_get_position_mm(&reprap.step_z));
221         c3mat4 headmove = translation3D(headp);
222         c3transform_set(head->transform.e[0], &headmove);
223
224         if (c3->root->dirty) {
225         //      printf("reproject head %.2f,%.2f,%.2f\n", headp.x, headp.y,headp.z);
226                 c3context_project(c3);
227         }
228         float z_min = c3context_view_get(c3)->z.min,
229                         z_max = c3context_view_get(c3)->z.max;
230         if (z_min < 0)
231                 z_min = 10;
232         z_min = 10;
233         if (z_max < z_min || z_max > 1000)
234                 z_max = 1000;
235
236         glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
237         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
238
239         // Set up projection matrix
240         glMatrixMode(GL_PROJECTION); // Select projection matrix
241         glLoadIdentity(); // Start with an identity matrix
242
243         gluPerspective(50, (float)_w / (float)_h, z_min, z_max);
244 #if 0
245         glCullFace(GL_BACK);
246         glEnable(GL_CULL_FACE);
247 #endif
248         glDepthMask(GL_TRUE);
249         glDepthFunc(GL_LEQUAL);
250         glEnable(GL_DEPTH_TEST);
251         glEnable(GL_LIGHTING);
252 //      glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
253
254         glEnable(GL_ALPHA_TEST);
255         glAlphaFunc(GL_GREATER, 1.0f / 255.0f);
256         glEnable(GL_BLEND); // Enable Blending
257         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Type Of Blending To Use
258
259         glMatrixMode(GL_MODELVIEW);
260         glLoadIdentity();
261
262         glMultMatrixf(c3context_view_get(c3)->cam.mtx.n);
263         glTranslatef(-c3context_view_get(c3)->cam.eye.n[VX],
264                         -c3context_view_get(c3)->cam.eye.n[VY],
265                         -c3context_view_get(c3)->cam.eye.n[VZ]);
266
267         dumpError("flush");
268
269         c3context_draw(c3);
270
271 #if FBO
272         /*
273          * Draw back FBO over the screen
274          */
275         glBindFramebuffer(GL_FRAMEBUFFER, 0);
276         dumpError("glBindFramebuffer 0");
277
278         glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
279         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
280 #endif
281         glDisable(GL_DEPTH_TEST);
282         glDisable(GL_LIGHTING);
283         glDisable(GL_ALPHA_TEST);
284
285         glUseProgram(0);
286
287         glMatrixMode(GL_PROJECTION); // Select projection matrix
288         glLoadIdentity(); // Start with an identity matrix
289         glOrtho(0, _w, 0, _h, 0, 10);
290         glScalef(1, -1, 1);
291         glTranslatef(0, -1 * _h, 0);
292         glMatrixMode(GL_MODELVIEW); // Select modelview matrix
293         glLoadIdentity(); // Start with an identity matrix
294
295         if (hud)
296                 c3context_draw(hud);
297
298     glutSwapBuffers();
299 }
300
301 #if !defined(GLUT_WHEEL_UP)
302 #  define GLUT_WHEEL_UP   3
303 #  define GLUT_WHEEL_DOWN 4
304 #endif
305
306
307 int button;
308 c3vec2 move;
309
310 static
311 void _gl_button_cb(
312                 int b,
313                 int s,
314                 int x,
315                 int y)
316 {
317         button = s == GLUT_DOWN ? b : 0;
318         move = c3vec2f(x, y);
319         c3context_view_p view = c3context_view_get_at(c3, 0);
320 //      printf("button %d: %.1f,%.1f\n", b, move.x, move.y);
321         switch (b) {
322                 case GLUT_LEFT_BUTTON:
323                 case GLUT_RIGHT_BUTTON: // call motion
324                         break;
325                 case GLUT_WHEEL_UP:
326                 case GLUT_WHEEL_DOWN:
327                         if (view->cam.distance > 10) {
328                                 const float d = 0.004;
329                                 c3cam_set_distance(&view->cam,
330                                                 view->cam.distance * ((b == GLUT_WHEEL_DOWN) ? (1.0+d) : (1.0-d)));
331                                 c3cam_update_matrix(&view->cam);
332                                 view->dirty = 1;        // resort the array
333                         }
334                         break;
335         }
336 }
337
338 void
339 _gl_motion_cb(
340                 int x,
341                 int y)
342 {
343         c3vec2 m = c3vec2f(x, y);
344         c3vec2 delta = c3vec2_sub(move, m);
345         c3context_view_p view = c3context_view_get_at(c3, 0);
346
347 //      printf("%s b%d click %.1f,%.1f now %d,%d delta %.1f,%.1f\n",
348 //                      __func__, button, move.n[0], move.n[1], x, y, delta.x, delta.y);
349
350         switch (button) {
351                 case GLUT_LEFT_BUTTON: {
352                         c3mat4 rotx = rotation3D(view->cam.side, delta.n[1] / 4);
353                         c3mat4 roty = rotation3D(c3vec3f(0.0, 0.0, 1.0), delta.n[0] / 4);
354                         rotx = c3mat4_mul(&rotx, &roty);
355                         c3cam_rot_about_lookat(&view->cam, &rotx);
356                         c3cam_update_matrix(&view->cam);
357
358                         view->dirty = 1;        // resort the array
359                 }       break;
360                 case GLUT_RIGHT_BUTTON: {
361                         // offset both points, but following the plane
362                         c3vec3 f = c3vec3_mulf(
363                                         c3vec3f(-view->cam.side.y, view->cam.side.x, 0),
364                                         -delta.n[1] / 4);
365                         view->cam.eye = c3vec3_add(view->cam.eye, f);
366                         view->cam.lookat = c3vec3_add(view->cam.lookat, f);
367                         c3cam_movef(&view->cam, delta.n[0] / 8, 0, 0);
368                         c3cam_update_matrix(&view->cam);
369
370                     view->dirty = 1;    // resort the array
371                 }       break;
372         }
373         move = m;
374 }
375
376 // gl timer. if the lcd is dirty, refresh display
377 static void
378 _gl_timer_cb(
379                 int i)
380 {
381         glutTimerFunc(1000 / 24, _gl_timer_cb, 0);
382         glutPostRedisplay();
383 }
384
385 const c3driver_context_t * c3_driver_list[3] = { NULL, NULL };
386
387 int
388 gl_init(
389                 int argc,
390                 char *argv[] )
391 {
392         glutInit(&argc, argv);          /* initialize GLUT system */
393
394         glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_ALPHA);
395         glutInitWindowSize(_w, _h);             /* width=400pixels height=500pixels */
396         /*window =*/ glutCreateWindow("Press 'q' to quit");     /* create window */
397
398         glutDisplayFunc(_gl_display_cb);                /* set window's display callback */
399         glutKeyboardFunc(_gl_key_cb);           /* set window's key callback */
400         glutTimerFunc(1000 / 24, _gl_timer_cb, 0);
401
402         glutMouseFunc(_gl_button_cb);
403         glutMotionFunc(_gl_motion_cb);
404     glutReshapeFunc(_gl_reshape_cb);
405
406         glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
407         glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
408         /*
409         glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
410         glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
411         glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
412         glEnable(GL_LINE_SMOOTH);
413          */
414         // enable color tracking
415         glEnable(GL_COLOR_MATERIAL);
416         // set material properties which will be assigned by glColor
417         glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
418
419         /* setup some lights */
420         glShadeModel(GL_SMOOTH);
421         glEnable(GL_LIGHTING);
422         GLfloat global_ambient[] = { 0.5f, 0.5f, 0.5f, 1.0f };
423         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);
424
425         {
426                 GLfloat specular[] = {1.0f, 1.0f, 1.0f , 0.8f};
427                 GLfloat position[] = { -30.0f, -30.0f, 200.0f, 1.0f };
428                 glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
429                 glLightfv(GL_LIGHT0, GL_POSITION, position);
430                 glEnable(GL_LIGHT0);
431         }
432         {
433                 GLfloat specular[] = {1.0f, 1.0f, 1.0f , 0.8f};
434                 GLfloat position[] = { 250.0f, -50.0f, 100.0f, 1.0f };
435                 glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
436                 glLightfv(GL_LIGHT0, GL_POSITION, position);
437                 glEnable(GL_LIGHT0);
438         }
439
440         /*
441          * Extract the GLSL version as a nuneric value for later
442          */
443         const char * glsl = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
444         {
445                 int M = 0, m = 0;
446                 if (sscanf(glsl, "%d.%d", &M, &m) == 2)
447                         glsl_version = (M * 100) + m;
448
449         }
450         printf("GL_SHADING_LANGUAGE_VERSION %s = %d\n", glsl, glsl_version);
451
452         gl_offscreenInit(_w, _h);
453
454         c3_driver_list[0] = c3gl_getdriver();
455
456     c3 = c3context_new(_w, _h);
457     c3->driver = c3_driver_list;
458
459     c3cam_p cam = &c3context_view_get_at(c3, 0)->cam;
460         cam->lookat = c3vec3f(100.0, 100.0, 0.0);
461         cam->eye = c3vec3f(100.0, -100.0, 100.0);
462
463     {
464         const char *path = "gfx/hb.png";
465         cairo_surface_t * image = cairo_image_surface_create_from_png (path);
466         printf("image = %p %p\n", image, cairo_image_surface_get_data (image));
467         c3texture_p b = c3texture_new(c3->root);
468
469         c3pixels_p dst = c3pixels_new(
470                         cairo_image_surface_get_width (image),
471                         cairo_image_surface_get_height (image),
472                         4, cairo_image_surface_get_stride(image),
473                         cairo_image_surface_get_data (image));
474                 dst->name = str_new(path);
475         b->geometry.mat.texture = dst;
476         b->size = c3vec2f(200, 200);
477                 b->geometry.mat.color = c3vec4f(1.0, 1.0, 1.0, 1.0);
478 //          c3transform_new(head);
479     }
480     c3pixels_p line_aa_tex = NULL;
481     {
482         const char *path = "gfx/BlurryCircle.png";
483         cairo_surface_t * image = cairo_image_surface_create_from_png (path);
484         printf("image = %p %p\n", image, cairo_image_surface_get_data (image));
485
486 #if 0
487         c3pixels_p dst = &b->pixels;
488         c3pixels_init(dst,
489                         cairo_image_surface_get_width (image),
490                         cairo_image_surface_get_height (image),
491                         1, cairo_image_surface_get_width (image),
492                         NULL);
493         c3pixels_alloc(dst);
494         b->size = c3vec2f(32, 32);
495         b->normalized = 1;
496
497         c3pixels_p src = c3pixels_new(
498                         cairo_image_surface_get_width (image),
499                         cairo_image_surface_get_height (image),
500                         4, cairo_image_surface_get_stride(image),
501                         cairo_image_surface_get_data (image));
502
503         uint32_t * _s = (uint32_t *)src->base;
504         uint8_t * _d = (uint8_t *)dst->base;
505         int max = 0;
506         for (int i = 0; i < dst->h * dst->w; i++)
507                 if ((_s[i] & 0xff) > max)
508                         max = _s[i] & 0xff;
509         for (int i = 0; i < dst->h * dst->w; i++)
510                 *_d++ = ((_s[i] & 0xff) * 255) / max;// + (0xff - max);
511         b->pixels.format = C3PIXEL_A;
512 #else
513         c3pixels_p dst = c3pixels_new(
514                         cairo_image_surface_get_width (image),
515                         cairo_image_surface_get_height (image),
516                         4, cairo_image_surface_get_stride(image),
517                         cairo_image_surface_get_data (image));
518         dst->format = C3PIXEL_ARGB;
519         dst->normalize = 1;
520         dst->name = str_new(path);
521         uint8_t * line = dst->base;
522         for (int y = 0; y < dst->h; y++, line += dst->row) {
523                 uint32_t *p = (uint32_t *)line;
524                 for (int x = 0; x < dst->w; x++, p++) {
525                         uint8_t b = *p;
526                         *p = ((0xff - b) << 24);//|(b << 16)|(b << 8)|(b);
527                 }
528         }
529 #endif
530         line_aa_tex = dst;
531 #if 0
532         c3pixels_p p = dst;
533         printf("struct { int w, h, stride, size, format; uint8_t pix[] } img = {\n"
534                         "%d, %d, %d, %d, %d\n",
535                         p->w, p->h, (int)p->row, p->psize, cairo_image_surface_get_format(image));
536         for (int i = 0; i < 32; i++)
537                 printf("0x%08x ", ((uint32_t*)p->base)[i]);
538         printf("\n");
539 #endif
540     }
541     c3object_p grid = c3object_new(c3->root);
542     {
543         for (int x = 0; x <= 20; x++) {
544                 for (int y = 0; y <= 20; y++) {
545                         c3vec3 p[4] = {
546                                 c3vec3f(-1+x*10,y*10,0.01), c3vec3f(1+x*10,y*10,0.01),
547                                 c3vec3f(x*10,-1+y*10,0.02), c3vec3f(x*10,1+y*10,0.02),
548                         };
549                 c3geometry_p g = c3geometry_new(
550                                 c3geometry_type(C3_LINES_TYPE, 0), grid);
551                 g->mat.color = c3vec4f(0.0, 0.0, 0.0, 0.8);
552                 g->mat.texture = line_aa_tex;
553                         c3lines_init(g, p, 4, 0.2);
554                 }
555         }
556     }
557     {   // light bulb
558         c3geometry_p g = c3sphere_uv(c3->root, c3vec3f(-30.0f, -20.0f, 200.0f), 3, 10, 10);
559         g->mat.color = c3vec4f(1.0, 1.0, 0.0, 1.0);
560     }
561
562    if (0) {
563                 c3vec3 p[4] = {
564                         c3vec3f(-5,-5,1), c3vec3f(205,-5,1),
565                 };
566         c3geometry_p g = c3geometry_new(
567                         c3geometry_type(C3_LINES_TYPE, 0), grid);
568         g->mat.color = c3vec4f(0.0, 0.0, 0.0, 1.0);
569         g->mat.texture = line_aa_tex;
570         g->line.width = 2;
571
572                 c3vertex_array_insert(&g->vertice,
573                                 g->vertice.count, p, 2);
574
575     }
576     head = c3stl_load("gfx/buserror-nozzle-model.stl", c3->root);
577     //head = c3object_new(c3->root);
578     c3transform_new(head);
579     if (head->geometry.count > 0) {
580         c3geometry_factor(head->geometry.e[0], 0.1, (20 * M_PI) / 180.0);
581         head->geometry.e[0]->mat.color = c3vec4f(0.6, 0.5, 0.0, 1.0);
582     }
583
584 #if 0
585     c3texture_p b = c3texture_new(head);
586     c3pixels_init(&b->pixels, 64, 64, 4, 4 * 64, NULL);
587     b->geometry.dirty = 1;
588     memset(b->pixels.base, 0xff, 10 * b->pixels.row);
589 #endif
590
591
592     hud = c3context_new(_w, _h);
593     hud->driver = c3_driver_list;
594
595     /*
596      * This is the offscreen framebuffer where the 3D scene is drawn
597      */
598     if (FBO) {
599         /*
600          * need to insert a header since there is nothing to detect the version number
601          * reliably without it, and __VERSION__ returns idiocy
602          */
603         char head[128];
604         sprintf(head, "#version %d\n#define GLSL_VERSION %d\n", glsl_version, glsl_version);
605
606         fxaa = c3program_new("fxaa");
607         c3program_array_add(&hud->programs, fxaa);
608         c3program_load_shader(fxaa, GL_VERTEX_SHADER, head,
609                         "gfx/postproc.vs", C3_PROGRAM_LOAD_UNIFORM);
610         c3program_load_shader(fxaa, GL_FRAGMENT_SHADER, head,
611                         "gfx/postproc.fs", C3_PROGRAM_LOAD_UNIFORM);
612
613         c3texture_p b = c3texture_new(hud->root);
614
615         c3pixels_p dst = c3pixels_new(_w, _h, 4, _w * 4, NULL);
616                 dst->name = str_new("fbo");
617                 dst->texture = (c3apiobject_t)fbo_texture;
618                 dst->normalize = 1;
619                 dst->dirty = 0;
620         //      dst->trace = 1;
621         b->geometry.mat.texture = dst;
622         b->geometry.mat.program = fxaa;
623         b->size = c3vec2f(_w, _h);
624                 b->geometry.mat.color = c3vec4f(1.0, 1.0, 1.0, 1.0);
625                 fbo_c3 = b;
626     }
627
628     {
629                 c3vec3 p[4] = {
630                         c3vec3f(10,10,0), c3vec3f(800-10,10,0),
631                 };
632         c3geometry_p g = c3geometry_new(
633                         c3geometry_type(C3_LINES_TYPE, 0), hud->root);
634         g->mat.color = c3vec4f(0.5, 0.5, 1.0, .3f);
635         g->mat.texture = line_aa_tex;
636                 c3lines_init(g, p, 2, 10);
637     }
638         return 1;
639 }
640
641 void
642 gl_dispose()
643 {
644         c3context_dispose(c3);
645 }
646
647 int
648 gl_runloop()
649 {
650         glutMainLoop();
651         gl_dispose();
652         return 0;
653 }