reprap: Updated to reflects changes to libc3
[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 "c3light.h"
47 #include "c3program.h"
48 #include "c3gl.h"
49 #include "c3gl_fbo.h"
50
51 #include <cairo/cairo.h>
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 c3gl_fbo_t fbo;
64
65 int glsl_version = 110;
66
67 extern reprap_t reprap;
68
69 static int dumpError(const char * what)
70 {
71         GLenum e;
72         int count = 0;
73         while ((e = glGetError()) != GL_NO_ERROR) {
74                 printf("%s: %s\n", what, gluErrorString(e));
75                 count++;
76         }
77         return count;
78 }
79
80 #define GLCHECK(_w) {_w; dumpError(#_w);}
81
82
83 static void
84 _gl_reshape_cb(int w, int h)
85 {
86     _w  = w;
87     _h = h;
88
89         c3vec2 size = c3vec2f(_w, _h);
90
91         glBindFramebuffer(GL_FRAMEBUFFER, 0);
92     glViewport(0, 0, _w, _h);
93     c3gl_fbo_resize(&fbo, size);
94     c3texture_resize(fbo_c3, size);
95
96     if (fxaa) {
97         glUseProgram((GLuint)fxaa->pid);
98         GLCHECK(glUniform2fv((GLuint)fxaa->params.e[0].pid, 1, size.n));
99         glUseProgram(0);
100     }
101
102     glutPostRedisplay();
103 }
104
105 static void
106 _gl_key_cb(
107                 unsigned char key,
108                 int x,
109                 int y)  /* called on key press */
110 {
111         switch (key) {
112                 case 'q':
113                 //      avr_vcd_stop(&vcd_file);
114                         c3context_dispose(c3);
115                         exit(0);
116                         break;
117                 case 'r':
118                         printf("Starting VCD trace; press 's' to stop\n");
119                 //      avr_vcd_start(&vcd_file);
120                         break;
121                 case 's':
122                         printf("Stopping VCD trace\n");
123                 //      avr_vcd_stop(&vcd_file);
124                         break;
125                 case '1':
126                         if (fbo_c3->geometry.mat.program)
127                                 fbo_c3->geometry.mat.program = NULL;
128                         else
129                                 fbo_c3->geometry.mat.program = fxaa;
130                         glutPostRedisplay();
131                         break;
132         }
133 }
134
135 static void
136 _gl_display_cb(void)            /* function called whenever redisplay needed */
137 {
138         /*
139          * Draw in FBO object
140          */
141         glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)fbo.fbo);
142         // draw (without glutSwapBuffers)
143         dumpError("glBindFramebuffer fbo");
144         glViewport(0, 0, _w, _h);
145
146         c3context_view_set(c3, 0);
147         c3vec3 headp = c3vec3f(
148                         stepper_get_position_mm(&reprap.step_x),
149                         stepper_get_position_mm(&reprap.step_y),
150                         stepper_get_position_mm(&reprap.step_z));
151         c3mat4 headmove = translation3D(headp);
152         c3transform_set(head->transform.e[0], &headmove);
153
154         if (c3->root->dirty) {
155         //      printf("reproject head %.2f,%.2f,%.2f\n", headp.x, headp.y,headp.z);
156                 c3context_project(c3);
157         }
158         float z_min = c3context_view_get(c3)->z.min,
159                         z_max = c3context_view_get(c3)->z.max;
160         if (z_min < 0)
161                 z_min = 10;
162         z_min = 10;
163         if (z_max < z_min || z_max > 1000)
164                 z_max = 1000;
165
166         glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
167         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
168
169         // Set up projection matrix
170         glMatrixMode(GL_PROJECTION); // Select projection matrix
171         c3mat4 p = perspective3D(50, (float)_w / (float)_h, z_min, z_max);
172         glLoadMatrixf(p.n);
173
174 #if 0
175         glCullFace(GL_BACK);
176         glEnable(GL_CULL_FACE);
177 #endif
178         glDepthMask(GL_TRUE);
179         glDepthFunc(GL_LEQUAL);
180         glEnable(GL_DEPTH_TEST);
181         glEnable(GL_LIGHTING);
182 //      glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
183
184         glEnable(GL_ALPHA_TEST);
185         glAlphaFunc(GL_GREATER, 1.0f / 255.0f);
186         glEnable(GL_BLEND); // Enable Blending
187         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Type Of Blending To Use
188
189         glMatrixMode(GL_MODELVIEW);
190
191         c3context_draw(c3);
192
193         /*
194          * Draw back FBO over the screen
195          */
196         glBindFramebuffer(GL_FRAMEBUFFER, 0);
197         dumpError("glBindFramebuffer 0");
198         glViewport(0, 0, _w, _h);
199
200         glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
201         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
202
203         glDisable(GL_DEPTH_TEST);
204         glDisable(GL_LIGHTING);
205         glDisable(GL_ALPHA_TEST);
206
207         glUseProgram(0);
208
209         glMatrixMode(GL_PROJECTION); // Select projection matrix
210         glLoadIdentity(); // Start with an identity matrix
211
212         c3mat4 pro = screen_ortho3D(0, _w, 0, _h, 0, 10);
213         glLoadMatrixf(pro.n);
214
215         glMatrixMode(GL_MODELVIEW); // Select modelview matrix
216
217         if (hud->root->dirty) {
218         //      printf("reproject head %.2f,%.2f,%.2f\n", headp.x, headp.y,headp.z);
219                 c3context_project(hud);
220         }
221         c3context_draw(hud);
222
223     glutSwapBuffers();
224 }
225
226 #if !defined(GLUT_WHEEL_UP)
227 #  define GLUT_WHEEL_UP   3
228 #  define GLUT_WHEEL_DOWN 4
229 #endif
230
231
232 int button;
233 c3vec2 move;
234
235 static
236 void _gl_button_cb(
237                 int b,
238                 int s,
239                 int x,
240                 int y)
241 {
242         button = s == GLUT_DOWN ? b : 0;
243         move = c3vec2f(x, y);
244         c3context_view_p view = c3context_view_get_at(c3, 0);
245 //      printf("button %d: %.1f,%.1f\n", b, move.x, move.y);
246         switch (b) {
247                 case GLUT_LEFT_BUTTON:
248                 case GLUT_RIGHT_BUTTON: // call motion
249                         break;
250                 case GLUT_WHEEL_UP:
251                 case GLUT_WHEEL_DOWN:
252                         if (view->cam.distance > 10) {
253                                 const float d = 0.004;
254                                 c3cam_set_distance(&view->cam,
255                                                 view->cam.distance * ((b == GLUT_WHEEL_DOWN) ? (1.0+d) : (1.0-d)));
256                                 c3cam_update_matrix(&view->cam);
257                                 view->dirty = 1;        // resort the array
258                         }
259                         break;
260         }
261 }
262
263 void
264 _gl_motion_cb(
265                 int x,
266                 int y)
267 {
268         c3vec2 m = c3vec2f(x, y);
269         c3vec2 delta = c3vec2_sub(move, m);
270         c3context_view_p view = c3context_view_get_at(c3, 0);
271
272 //      printf("%s b%d click %.1f,%.1f now %d,%d delta %.1f,%.1f\n",
273 //                      __func__, button, move.n[0], move.n[1], x, y, delta.x, delta.y);
274
275         switch (button) {
276                 case GLUT_LEFT_BUTTON: {
277                         c3mat4 rotx = rotation3D(view->cam.side, delta.n[1] / 4);
278                         c3mat4 roty = rotation3D(c3vec3f(0.0, 0.0, 1.0), delta.n[0] / 4);
279                         rotx = c3mat4_mul(&rotx, &roty);
280                         c3cam_rot_about_lookat(&view->cam, &rotx);
281                         c3cam_update_matrix(&view->cam);
282
283                         view->dirty = 1;        // resort the array
284                 }       break;
285                 case GLUT_RIGHT_BUTTON: {
286                         // offset both points, but following the plane
287                         c3vec3 f = c3vec3_mulf(
288                                         c3vec3f(-view->cam.side.y, view->cam.side.x, 0),
289                                         -delta.n[1] / 4);
290                         view->cam.eye = c3vec3_add(view->cam.eye, f);
291                         view->cam.lookat = c3vec3_add(view->cam.lookat, f);
292                         c3cam_movef(&view->cam, delta.n[0] / 8, 0, 0);
293                         c3cam_update_matrix(&view->cam);
294
295                     view->dirty = 1;    // resort the array
296                 }       break;
297         }
298         move = m;
299 }
300
301 // gl timer. if the lcd is dirty, refresh display
302 static void
303 _gl_timer_cb(
304                 int i)
305 {
306         glutTimerFunc(1000 / 24, _gl_timer_cb, 0);
307         glutPostRedisplay();
308 }
309
310 const c3driver_context_t * c3_driver_list[3] = { NULL, NULL };
311
312 int
313 gl_init(
314                 int argc,
315                 char *argv[] )
316 {
317         glutInit(&argc, argv);          /* initialize GLUT system */
318
319         glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_ALPHA);
320         glutInitWindowSize(_w, _h);             /* width=400pixels height=500pixels */
321         /*window =*/ glutCreateWindow("Press 'q' to quit");     /* create window */
322
323         glutDisplayFunc(_gl_display_cb);                /* set window's display callback */
324         glutKeyboardFunc(_gl_key_cb);           /* set window's key callback */
325         glutTimerFunc(1000 / 24, _gl_timer_cb, 0);
326
327         glutMouseFunc(_gl_button_cb);
328         glutMotionFunc(_gl_motion_cb);
329     glutReshapeFunc(_gl_reshape_cb);
330
331         glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
332         glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
333
334         // enable color tracking
335         glEnable(GL_COLOR_MATERIAL);
336         // set material properties which will be assigned by glColor
337         glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
338
339         /* setup some lights */
340         glShadeModel(GL_SMOOTH);
341         glEnable(GL_LIGHTING);
342         GLfloat global_ambient[] = { 0.5f, 0.5f, 0.5f, 1.0f };
343         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);
344
345         if (0) {
346                 GLfloat specular[] = {1.0f, 1.0f, 1.0f , 0.8f};
347                 GLfloat position[] = { 250.0f, -50.0f, 100.0f, 1.0f };
348                 glLightfv(GL_LIGHT1, GL_SPECULAR, specular);
349                 glLightfv(GL_LIGHT1, GL_POSITION, position);
350                 glEnable(GL_LIGHT1);
351         }
352
353         /*
354          * Extract the GLSL version as a numeric value for later
355          */
356         const char * glsl = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
357         {
358                 int M = 0, m = 0;
359                 if (sscanf(glsl, "%d.%d", &M, &m) == 2)
360                         glsl_version = (M * 100) + m;
361
362         }
363         printf("GL_SHADING_LANGUAGE_VERSION %s = %d\n", glsl, glsl_version);
364
365         c3gl_fbo_create(&fbo, c3vec2f(_w, _h), (1 << C3GL_FBO_COLOR)|(1 << C3GL_FBO_DEPTH));
366
367         c3_driver_list[0] = c3gl_getdriver();
368
369     c3 = c3context_new(_w, _h);
370     c3->driver = c3_driver_list;
371
372     c3cam_p cam = &c3context_view_get_at(c3, 0)->cam;
373         cam->lookat = c3vec3f(100.0, 100.0, 0.0);
374         cam->eye = c3vec3f(100.0, -100.0, 100.0);
375
376         /*
377          * Create a light, attach it to a movable object, and attach a sphere
378          * to it too so it's visible.
379          */
380         {
381                 c3object_p ligthhook = c3object_new(c3->root);
382             c3transform_p pos = c3transform_new(ligthhook);
383
384             pos->matrix = translation3D(c3vec3f(-30.0f, -30.0f, 200.0f));
385
386                 c3light_p light = c3light_new(ligthhook);
387                 light->geometry.name = str_new("light0");
388                 light->color.specular = c3vec4f(1.0f, 1.0f, 1.0f , 0.8f);
389                 light->position = c3vec4f(0, 0, 0, 1.0f );
390
391             {   // light bulb
392                 c3geometry_p g = c3sphere_uv(ligthhook, c3vec3f(0, 0, 0), 3, 10, 10);
393                 g->mat.color = c3vec4f(1.0, 1.0, 0.0, 1.0);
394                 g->hidden = 0;  // hidden from light scenes
395             }
396         }
397
398     {
399         const char *path = "gfx/hb.png";
400         cairo_surface_t * image = cairo_image_surface_create_from_png (path);
401         printf("image = %p %p\n", image, cairo_image_surface_get_data (image));
402         c3texture_p b = c3texture_new(c3->root);
403
404         c3pixels_p dst = c3pixels_new(
405                         cairo_image_surface_get_width (image),
406                         cairo_image_surface_get_height (image),
407                         4, cairo_image_surface_get_stride(image),
408                         cairo_image_surface_get_data (image));
409                 dst->name = str_new(path);
410         b->geometry.mat.texture = dst;
411         b->size = c3vec2f(200, 200);
412                 b->geometry.mat.color = c3vec4f(1.0, 1.0, 1.0, 1.0);
413 //          c3transform_new(head);
414     }
415     c3pixels_p line_aa_tex = NULL;
416     {
417         const char *path = "gfx/BlurryCircle.png";
418         cairo_surface_t * image = cairo_image_surface_create_from_png (path);
419         printf("image = %p %p\n", image, cairo_image_surface_get_data (image));
420
421 #if 0
422         c3pixels_p dst = &b->pixels;
423         c3pixels_init(dst,
424                         cairo_image_surface_get_width (image),
425                         cairo_image_surface_get_height (image),
426                         1, cairo_image_surface_get_width (image),
427                         NULL);
428         c3pixels_alloc(dst);
429         b->size = c3vec2f(32, 32);
430         b->normalized = 1;
431
432         c3pixels_p src = c3pixels_new(
433                         cairo_image_surface_get_width (image),
434                         cairo_image_surface_get_height (image),
435                         4, cairo_image_surface_get_stride(image),
436                         cairo_image_surface_get_data (image));
437
438         uint32_t * _s = (uint32_t *)src->base;
439         uint8_t * _d = (uint8_t *)dst->base;
440         int max = 0;
441         for (int i = 0; i < dst->h * dst->w; i++)
442                 if ((_s[i] & 0xff) > max)
443                         max = _s[i] & 0xff;
444         for (int i = 0; i < dst->h * dst->w; i++)
445                 *_d++ = ((_s[i] & 0xff) * 255) / max;// + (0xff - max);
446         b->pixels.format = C3PIXEL_A;
447 #else
448         c3pixels_p dst = c3pixels_new(
449                         cairo_image_surface_get_width (image),
450                         cairo_image_surface_get_height (image),
451                         4, cairo_image_surface_get_stride(image),
452                         cairo_image_surface_get_data (image));
453         dst->format = C3PIXEL_ARGB;
454         dst->normalize = 1;
455         dst->name = str_new(path);
456         uint8_t * line = dst->base;
457         for (int y = 0; y < dst->h; y++, line += dst->row) {
458                 uint32_t *p = (uint32_t *)line;
459                 for (int x = 0; x < dst->w; x++, p++) {
460                         uint8_t b = *p;
461                         *p = ((0xff - b) << 24);//|(b << 16)|(b << 8)|(b);
462                 }
463         }
464 #endif
465         line_aa_tex = dst;
466 #if 0
467         c3pixels_p p = dst;
468         printf("struct { int w, h, stride, size, format; uint8_t pix[] } img = {\n"
469                         "%d, %d, %d, %d, %d\n",
470                         p->w, p->h, (int)p->row, p->psize, cairo_image_surface_get_format(image));
471         for (int i = 0; i < 32; i++)
472                 printf("0x%08x ", ((uint32_t*)p->base)[i]);
473         printf("\n");
474 #endif
475     }
476     c3object_p grid = c3object_new(c3->root);
477     {
478         for (int x = 0; x <= 20; x++) {
479                 for (int y = 0; y <= 20; y++) {
480                         c3vec3 p[4] = {
481                                 c3vec3f(-1+x*10,y*10,0.01), c3vec3f(1+x*10,y*10,0.01),
482                                 c3vec3f(x*10,-1+y*10,0.02), c3vec3f(x*10,1+y*10,0.02),
483                         };
484                 c3geometry_p g = c3geometry_new(
485                                 c3geometry_type(C3_LINES_TYPE, 0), grid);
486                 g->mat.color = c3vec4f(0.0, 0.0, 0.0, 0.8);
487                 g->mat.texture = line_aa_tex;
488                         c3lines_init(g, p, 4, 0.2);
489                 }
490         }
491     }
492
493    if (0) {
494                 c3vec3 p[4] = {
495                         c3vec3f(-5,-5,1), c3vec3f(205,-5,1),
496                 };
497         c3geometry_p g = c3geometry_new(
498                         c3geometry_type(C3_LINES_TYPE, 0), grid);
499         g->mat.color = c3vec4f(0.0, 0.0, 0.0, 1.0);
500         g->mat.texture = line_aa_tex;
501         g->line.width = 2;
502
503                 c3vertex_array_insert(&g->vertice,
504                                 g->vertice.count, p, 2);
505
506     }
507     head = c3stl_load("gfx/buserror-nozzle-model.stl", c3->root);
508     //head = c3object_new(c3->root);
509     c3transform_new(head);
510     if (head->geometry.count > 0) {
511         c3geometry_factor(head->geometry.e[0], 0.1, (20 * M_PI) / 180.0);
512         head->geometry.e[0]->mat.color = c3vec4f(0.6, 0.5, 0.0, 1.0);
513     }
514
515 #if 0
516     c3texture_p b = c3texture_new(head);
517     c3pixels_init(&b->pixels, 64, 64, 4, 4 * 64, NULL);
518     b->geometry.dirty = 1;
519     memset(b->pixels.base, 0xff, 10 * b->pixels.row);
520 #endif
521
522
523     hud = c3context_new(_w, _h);
524     hud->driver = c3_driver_list;
525
526     /*
527      * This is the offscreen framebuffer where the 3D scene is drawn
528      */
529     {
530         /*
531          * need to insert a header since there is nothing to detect the version number
532          * reliably without it, and __VERSION__ returns idiocy
533          */
534         char head[128];
535         sprintf(head, "#version %d\n#define GLSL_VERSION %d\n", glsl_version, glsl_version);
536
537         const char *uniforms[] = { "g_Resolution", NULL };
538         fxaa = c3program_new("fxaa", uniforms);
539         c3program_array_add(&hud->programs, fxaa);
540         c3program_load_shader(fxaa, GL_VERTEX_SHADER, head,
541                         "gfx/postproc.vs", C3_PROGRAM_LOAD_UNIFORM);
542         c3program_load_shader(fxaa, GL_FRAGMENT_SHADER, head,
543                         "gfx/postproc.fs", C3_PROGRAM_LOAD_UNIFORM);
544
545         c3texture_p b = c3texture_new(hud->root);
546
547         c3pixels_p dst = c3pixels_new(_w, _h, 4, _w * 4, NULL);
548                 dst->name = str_new("fbo");
549                 dst->texture = fbo.buffers[C3GL_FBO_COLOR].bid;
550                 dst->normalize = 1;
551                 dst->dirty = 0;
552         //      dst->trace = 1;
553         b->geometry.mat.texture = dst;
554         b->geometry.mat.program = fxaa;
555         b->size = c3vec2f(_w, _h);
556                 b->geometry.mat.color = c3vec4f(1.0, 1.0, 1.0, 1.0);
557                 fbo_c3 = b;
558     }
559
560     {
561                 c3vec3 p[4] = {
562                         c3vec3f(10,10,0), c3vec3f(800-10,10,0),
563                 };
564         c3geometry_p g = c3geometry_new(
565                         c3geometry_type(C3_LINES_TYPE, 0), hud->root);
566         g->mat.color = c3vec4f(0.5, 0.5, 1.0, .3f);
567         g->mat.texture = line_aa_tex;
568                 c3lines_init(g, p, 2, 10);
569     }
570         return 1;
571 }
572
573 void
574 gl_dispose()
575 {
576         c3context_dispose(c3);
577         c3context_dispose(hud);
578         c3gl_fbo_dispose(&fbo);
579 }
580
581 int
582 gl_runloop()
583 {
584         glutMainLoop();
585         gl_dispose();
586         return 0;
587 }