rdex-client: display loaded textures the right way up
[rdex:client.git] / src / library.c
1 /* =====================================================================
2 rdex -- reaction-diffusion explorer
3 Copyright (C) 2008,2009  Claude Heiland-Allen <claudiusmaximus@goto10.org>
4 ------------------------------------------------------------------------
5 Library Module
6 ===================================================================== */
7
8 #include <assert.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <wordexp.h>
13 #include <math.h>
14 #include "library.h"
15 #include "upload.h"
16
17 struct library *library_init(struct library *library) {
18   if (!library) { return 0; }
19   if (! borderwindow_init(&library->borderwindow)) { return 0; }
20   library->count = 0;
21   library->arraysize = 256;
22   library->array = malloc(sizeof(struct libelem) * library->arraysize);
23   for (int i = 0; i < 4; ++i) { library->vmin[i] =  1.0e6; }
24   for (int i = 0; i < 4; ++i) { library->vmax[i] = -1.0e6; }
25   for (int i = 0; i < 4; ++i) { library->vsub[i] = 0; }
26   for (int i = 0; i < 4; ++i) { library->vmul[i] = 0; }
27   const GLfloat m0[16] = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
28   for (int i = 0; i < 16; ++i) { library->m[i] = m0[i]; }
29   library->d = 4;
30   library->frame = 0;
31   library->X = 0;
32   library->Y = 0;
33   library->Z = 0;
34   library->width = 1;
35   library->height = 1;
36   library->snap = 0;
37   library->image = image_alloc(128, 128, 3); //FIXME
38   tamura_init(&library->tamura, 128, 128); //FIXME
39   upload_init(&library->upload); //FIXME -- curl global init must be before any threads are created
40   library->buffersize = library_tex_size * library_tex_size * 3;
41   library->buffer = malloc(library->buffersize);
42   library->session = getenv("RDEX_SESSION");
43   if (! library->session) { library->session = "."; }
44   library_load(library);
45   return library;
46 }
47
48 void library_grow(struct library *library) {
49   int newarraysize = library->arraysize * 2;
50   struct libelem *newarray = malloc(sizeof(struct libelem) * newarraysize);
51   memcpy(newarray, library->array, sizeof(struct libelem) * library->count);
52   free(library->array);
53   library->arraysize = newarraysize;
54   library->array = newarray;
55 }
56
57 void library_append(
58   struct library *library,
59   GLfloat ru, GLfloat rv, GLfloat f, GLfloat k, GLuint t
60 ) {
61   assert(library->count < library->arraysize);
62   library->vmin[0] = fmin(library->vmin[0], ru);
63   library->vmin[1] = fmin(library->vmin[1], rv);
64   library->vmin[2] = fmin(library->vmin[2], f);
65   library->vmin[3] = fmin(library->vmin[3], k);
66   library->vmax[0] = fmax(library->vmax[0], ru);
67   library->vmax[1] = fmax(library->vmax[1], rv);
68   library->vmax[2] = fmax(library->vmax[2], f);
69   library->vmax[3] = fmax(library->vmax[3], k);
70   library->array[library->count].v[0] = ru;
71   library->array[library->count].v[1] = rv;
72   library->array[library->count].v[2] = f;
73   library->array[library->count].v[3] = k;
74   library->array[library->count].t = t;
75   library->count += 1;
76   if (library->count == library->arraysize) {
77     library_grow(library);
78   }
79 }
80
81 void library_rerange(struct library *library) {
82   for (int i = 0; i < 4; ++i) {
83     library->vmul[i] = library->vmax[i] - library->vmin[i];
84     library->vsub[i] = library->vmin[i] + library->vmul[i]/2;
85     if (library->vmul[i] > 0) { library->vmul[i] = 1.0/library->vmul[i]; }
86   }
87 }
88
89 int library_load(struct library *library) {
90   glEnable(GL_TEXTURE_2D);
91   wordexp_t p;
92   const char *pattern2 = "/ru_*__rv_*__f_*__k_*.ppm";
93   int patternlen = strlen(library->session) + strlen(pattern2) + 1;
94   char *pattern = malloc(patternlen);
95   snprintf(pattern, patternlen, "%s%s", library->session, pattern2);
96   if (0 == wordexp(pattern, &p, WRDE_NOCMD | WRDE_SHOWERR | WRDE_UNDEF)) {
97     char sru[9]; char srv[9]; char sf[9]; char sk[9];
98     for (int i = 0; i < p.we_wordc; ++i) {
99       int l = strlen(p.we_wordv[i]);
100       const char *pattern3 = "ru_%8c__rv_%8c__f_%8c__k_%8c.ppm";
101       if (
102         l >= 52 &&
103         4 == sscanf(p.we_wordv[i] + l - 52, pattern3, sru, srv, sf, sk)
104       ) {
105         sru[8] = '\0'; srv[8] = '\0'; sf[8] = '\0'; sk[8] = '\0';
106         double ru, rv, f, k;
107         if (1 != sscanf(sru, "%lg", &ru)) { break; }
108         if (1 != sscanf(srv, "%lg", &rv)) { break; }
109         if (1 != sscanf(sf,  "%lg", &f))  { break; }
110         if (1 != sscanf(sk,  "%lg", &k))  { break; }
111         FILE *image = fopen(p.we_wordv[i], "rb");
112         if (!image) { break; }
113         fseek(image, -library->buffersize, SEEK_END);
114         if (fread(library->buffer, library->buffersize, 1, image) != 1) {
115           fprintf(stderr, "warning: failed to read image?\n");
116         }
117         fclose(image);
118         GLuint t;
119         glGenTextures(1, &t);
120         glBindTexture(GL_TEXTURE_2D, t);
121         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
122         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
123         glTexImage2D(
124           GL_TEXTURE_2D, 0, GL_RGB,
125           library_tex_size, library_tex_size, 0,
126           GL_RGB, GL_UNSIGNED_BYTE, library->buffer
127         );
128         library_append(library, ru, rv, f, k, t);
129       }
130     }
131     wordfree(&p);
132   }
133   free(pattern);
134   library_rerange(library);
135   glDisable(GL_TEXTURE_2D);
136   return 1;
137 }
138
139 void library_display_save(
140   struct library *library, GLuint fbo, GLuint texture, GLuint rawtexture,
141   double ru, double rv, double f, double k, const char *behaviour
142 ) {
143   if (! library->snap) { return; }
144   library->snap = 0;
145
146   struct tamura_features feature[2];
147   {
148     image_copytexture(library->image, rawtexture);
149     tamura_calculate(&library->tamura, &feature[0], library->image);
150   }
151
152   GLuint t;
153   glGenTextures(1, &t);
154   glEnable(GL_TEXTURE_2D);
155   glBindTexture(GL_TEXTURE_2D, t);
156   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
157   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
158   glTexImage2D(
159     GL_TEXTURE_2D, 0, GL_RGBA,
160     library_tex_size, library_tex_size, 0,
161     GL_RGBA, GL_UNSIGNED_BYTE, 0
162   );
163   glMatrixMode(GL_PROJECTION);
164   glLoadIdentity();
165   glViewport(0, 0, library_tex_size, library_tex_size);
166   gluOrtho2D(0, 1, 0, 1);
167   glMatrixMode(GL_MODELVIEW);
168   glLoadIdentity();
169   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
170   glFramebufferTexture2DEXT(
171     GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
172     t, 0
173   );
174   glBindTexture(GL_TEXTURE_2D, texture);
175   glBegin(GL_QUADS); { glColor4f(1,1,1,1);
176     glTexCoord2f(0, 0); glVertex2f(0, 0);
177     glTexCoord2f(1, 0); glVertex2f(1, 0);
178     glTexCoord2f(1, 1); glVertex2f(1, 1);
179     glTexCoord2f(0, 1); glVertex2f(0, 1);
180   } glEnd();
181   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
182   char filename[1024];
183   snprintf(
184     filename, 1000,
185     "%s/ru_%f__rv_%f__f_%f__k_%f.ppm",  // FIXME ensure same stem
186     library->session, ru, rv, f, k
187   );
188   FILE *imgfile = fopen(filename, "wb");
189   char ppm[UPLOAD_DATA_PPMLEN];
190   int ppm_bytes = 0;
191   if (imgfile) {
192     glBindTexture(GL_TEXTURE_2D, t);
193     glGetTexImage(
194       GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, library->buffer
195     );
196     ppm_bytes = snprintf(ppm, UPLOAD_DATA_PPMLEN, "P6\n%d %d 255\n", library_tex_size, library_tex_size);
197     for (int y = library_tex_size - 1; y >= 0; --y) {
198       memcpy(ppm + ppm_bytes, library->buffer + y * library_tex_size * 3, library_tex_size * 3);
199       ppm_bytes += library_tex_size * 3;
200     }
201     fwrite(ppm, ppm_bytes, 1, imgfile);
202     fclose(imgfile);
203   }
204   struct histogram histogram;
205   struct segment segment;
206   struct image *image = image_alloc(library_tex_size, library_tex_size, 3);
207   image_copytexture(image, t);
208   histogram_calc(&histogram, image);
209   segment_calc(&segment, image);
210   image_free(image);
211   upload_enqueue(
212     &library->upload,
213     ru, rv, f, k, behaviour,
214     feature[0].coarseness, feature[0].contrast, feature[0].directionality,
215     feature[1].coarseness, feature[1].contrast, feature[1].directionality,
216     &histogram, &segment, ppm, ppm_bytes
217   );
218   library_append(library, ru, rv, f, k, t);
219   library_rerange(library);
220   glDisable(GL_TEXTURE_2D);
221 }
222
223 void library_reshape(struct library *library, int w, int h) {
224   library->width = w;
225   library->height = h;
226 }
227
228 void library_project(struct library *library) {
229   for (int i = 0; i < library->count; ++i) {
230     GLfloat x0 = (library->array[i].v[0] - library->vsub[0]) * library->vmul[0];
231     GLfloat x1 = (library->array[i].v[1] - library->vsub[1]) * library->vmul[1];
232     GLfloat x2 = (library->array[i].v[2] - library->vsub[2]) * library->vmul[2];
233     GLfloat x3 = (library->array[i].v[3] - library->vsub[3]) * library->vmul[3];
234     GLfloat y0 = library->m[ 0] * x0 + library->m[ 4]*x1 + library->m[ 8]*x2 + library->m[12]*x3;
235     GLfloat y1 = library->m[ 1] * x0 + library->m[ 5]*x1 + library->m[ 9]*x2 + library->m[13]*x3;
236     GLfloat y2 = library->m[ 2] * x0 + library->m[ 6]*x1 + library->m[10]*x2 + library->m[14]*x3;
237     GLfloat y3 = library->m[ 3] * x0 + library->m[ 7]*x1 + library->m[11]*x2 + library->m[15]*x3;
238     GLfloat k = 3 * library->d / (library->d + y3);
239     y0 *= k;
240     y1 *= k;
241     y2 *= k;
242     library->array[i].w[0] = y0;
243     library->array[i].w[1] = y1;
244     library->array[i].w[2] = y2;
245   }
246 }
247
248 void library_display(struct library *library, GLuint texture) {
249   library->X = sin(library->frame / 100.0);
250   library->Y = sin(library->frame / 141.4);
251   library->Z = sin(library->frame / 173.2);
252   glClearColor(0,0,0,1);
253   glClear(GL_COLOR_BUFFER_BIT);
254   int width, height;
255   glEnable(GL_TEXTURE_2D);
256   glBindTexture(GL_TEXTURE_2D, texture);
257   glMatrixMode(GL_PROJECTION);
258   glLoadIdentity();
259   gluOrtho2D(0, 1, 0, 1);
260   glMatrixMode(GL_MODELVIEW);
261   glLoadIdentity();
262   glViewport(0, 0, library->width, library->height);
263   glGetTexLevelParameteriv(
264     GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width
265   );
266   glGetTexLevelParameteriv(
267     GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height
268   );
269   glBegin(GL_QUADS); {
270     float x = 0.5 * library->width/width;
271     float y = 0.5 * library->height/height;
272     glColor4f(1,1,1,1);
273     glTexCoord2f(0.5 - x, 0.5 - y); glVertex2f(0, 0);
274     glTexCoord2f(0.5 + x, 0.5 - y); glVertex2f(1, 0);
275     glTexCoord2f(0.5 + x, 0.5 + y); glVertex2f(1, 1);
276     glTexCoord2f(0.5 - x, 0.5 + y); glVertex2f(0, 1);
277   } glEnd();
278   glMatrixMode(GL_PROJECTION);
279   glLoadIdentity();
280   float v = (float) library->width / (float) library->height;
281   glFrustum(-v, v, -1, 1, 4, 12);
282   glMatrixMode(GL_MODELVIEW);
283   glViewport(0, 0, library->width, library->height);
284   glLoadIdentity();
285   // { 4D rolling ball
286     float R = 256;
287     float r = sqrt(
288       library->X * library->X +
289       library->Y * library->Y +
290       library->Z * library->Z
291     );
292     float D = sqrt(R*R+r*r);
293     float c = R / D;
294     float s = r / D;
295     float x, y, z;
296     if (r > 0.00001) {
297       x = library->X / r;
298       y = library->Y / r;
299       z = library->Z / r;
300     } else {
301       x = 0;
302       y = 0;
303       z = 0;
304     }
305     GLfloat m[16] = {
306       1-x*x*(1-c), -(1-c)*x*y,  -(1-c)*x*z,  s*x,
307       -(1-c)*x*y,  1-y*y*(1-c), -(1-c)*y*z,  s*y,
308       -(1-c)*x*z,  -(1-c)*y*z,  1-z*z*(1-c), s*z,
309       -s*x,        -s*y,        -s*z,        c
310     };
311     glMultMatrixf(m);
312     glMultMatrixf(library->m);
313     glGetFloatv(GL_MODELVIEW_MATRIX , library->m);
314   // } rolling ball
315   glLoadIdentity();
316   glTranslatef(0,0,-8);
317   library_project(library);
318   glEnable(GL_DEPTH_TEST);
319   glClear(GL_DEPTH_BUFFER_BIT);
320   borderwindow_use(&library->borderwindow);
321   for (int i = 0; i < library->count; ++i) {
322     glBindTexture(GL_TEXTURE_2D, library->array[i].t);
323     GLfloat y0 = library->array[i].w[0];
324     GLfloat y1 = library->array[i].w[1];
325     GLfloat y2 = library->array[i].w[2];
326     glBegin(GL_QUADS); {
327       glColor4f(1, 1, 1, 1);
328       // note: y texcoord flipped for correct display from loaded image files
329       glTexCoord2f(0, 1); glVertex3f(y0 - 0.1, y1 - 0.1, y2);
330       glTexCoord2f(1, 1); glVertex3f(y0 + 0.1, y1 - 0.1, y2);
331       glTexCoord2f(1, 0); glVertex3f(y0 + 0.1, y1 + 0.1, y2);
332       glTexCoord2f(0, 0); glVertex3f(y0 - 0.1, y1 + 0.1, y2);
333     } glEnd();
334   }
335   borderwindow_use(0);
336   glDisable(GL_DEPTH_TEST);
337   glDisable(GL_TEXTURE_2D);
338   library->frame += 1;
339 }
340
341 void library_pmotion(struct library *library, int x, int y) {
342 /*
343   library->X =  (x - library->width/2.0)  / (float) library->width;
344   library->Y = -(y - library->height/2.0) / (float) library->height;
345   library->Z = 0;
346 */
347 }
348
349 void library_amotion(struct library *library, int x, int y) {
350 /*
351   library->X = (x - library->width/2.0)  / (float) library->width;
352   library->Y = 0;
353   library->Z = (y - library->height/2.0) / (float) library->height;
354 */
355 }
356
357 void library_idle(struct library *library) {
358 }
359
360 // EOF