1 #include "openglutilities.h"
2 #include "mathutilities.h"
3 #include "stringutilities.h"
4 #include "pmconstants.h"
5 #include "Report.h"
6 #include <vector>
7 #include <algorithm>
8 
9 #if defined(HAVE_OPENGL) || defined(HAVE_GLUT)
10 #include <GL/gl.h>
11 #endif
12 
13 using std::ostream;
14 using std::vector;
15 using std::string;
16 using std::sort;
17 using namespace ProtoMol;
18 using namespace ProtoMol::Report;
19 
20 namespace ProtoMol {
21 
22 #if defined(HAVE_OPENGL) || defined(HAVE_GLUT)
23 
24   static GLint bufferSize = 0;
25 
26   //_____________________________________________________________________ Feedback3Dcolor
27   struct Feedback3Dcolor {
28     GLfloat x;
29     GLfloat y;
30     GLfloat z;
31     GLfloat red;
32     GLfloat green;
33     GLfloat blue;
34     GLfloat alpha;
35   };
36   //_____________________________________________________________________ DepthIndex
37   struct DepthIndex {
DepthIndexProtoMol::DepthIndex38     DepthIndex():ptr(NULL),depth(0.0){}
DepthIndexProtoMol::DepthIndex39     DepthIndex(GLfloat *a, GLfloat b):ptr(a),depth(b){}
40 
41     GLfloat *ptr;
42     GLfloat depth;
operator <ProtoMol::DepthIndex43     bool operator<(const DepthIndex& a2) const{
44       return (a2.depth < depth);
45     }
46   };
47 
48   //_____________________________________________________________________ print3DcolorVertex
print3DcolorVertex(GLint size,GLint * count,GLfloat * buffer)49   static string print3DcolorVertex(GLint size, GLint * count, GLfloat * buffer){
50     string res = " ";
51     for (unsigned int i = 0; i < 7; i++) {
52       res += " "+toString(buffer[size - (*count)],4,2);
53       *count = *count - 1;
54     }
55     return res;
56   }
57 #endif
58 
59   //_____________________________________________________________________ openglToEPS
openglToPlain(ostream & output,void (* display)())60   unsigned int openglToPlain(ostream& output,void (*display)()){
61 #if defined(HAVE_OPENGL) || defined(HAVE_GLUT)
62     vector<GLfloat> feedbackBuffer(bufferSize);
63     while(true){
64       glFeedbackBuffer(feedbackBuffer.size(), GL_3D_COLOR, &(feedbackBuffer[0]));
65       glRenderMode(GL_FEEDBACK);
66       display();
67       GLint returned = glRenderMode(GL_RENDER);
68       if(returned > 0){
69 	feedbackBuffer.resize(returned);
70 	break;
71       }
72       bufferSize += 100*1024;
73       feedbackBuffer.resize(bufferSize);
74     }
75     GLint size = feedbackBuffer.size();
76     GLfloat* buffer = &(feedbackBuffer[0]);
77 
78     int token, nvertices;
79     GLint count = size;
80     while (count) {
81       token = static_cast<int>(buffer[size - count]);
82       count--;
83       switch (token) {
84       case GL_PASS_THROUGH_TOKEN:
85 	output << "GL_PASS_THROUGH_TOKEN\n";
86 	output << "  "<<toString(buffer[size - count],4,2) <<"\n";
87 	count--;
88 	break;
89       case GL_POINT_TOKEN:
90 	output << "GL_POINT_TOKEN\n";
91 	output << print3DcolorVertex(size, &count, buffer) <<"\n";
92 	break;
93       case GL_LINE_TOKEN:
94 	output << "GL_LINE_TOKEN\n";
95 	output << print3DcolorVertex(size, &count, buffer) <<"\n";
96 	output << print3DcolorVertex(size, &count, buffer) <<"\n";
97 	break;
98       case GL_LINE_RESET_TOKEN:
99 	output << "GL_LINE_RESET_TOKEN\n";
100 	output << print3DcolorVertex(size, &count, buffer) <<"\n";
101 	output << print3DcolorVertex(size, &count, buffer) <<"\n";
102 	break;
103       case GL_POLYGON_TOKEN:
104 	output << "GL_POLYGON_TOKEN\n";
105 	nvertices = static_cast<int>(buffer[size - count]);
106 	count--;
107 	for (; nvertices > 0; nvertices--) {
108 	  output << print3DcolorVertex(size, &count, buffer) <<"\n";
109 	}
110       default:
111 	break;
112       }
113     }
114     count = size;
115 #else
116     unsigned int count = 0;
117 
118 #endif
119     return count;
120   }
121 
122   //_____________________________________________________________________ openglToEPS
openglToEPS(ostream & output,void (* display)())123   unsigned int openglToEPS(ostream& output,void (*display)()){
124     unsigned int count = 0;
125 #if defined(HAVE_OPENGL) || defined(HAVE_GLUT)
126 
127     //
128     vector<GLfloat> feedbackBuffer(bufferSize);
129     while(true){
130       glFeedbackBuffer(feedbackBuffer.size(), GL_3D_COLOR, &(feedbackBuffer[0]));
131       glRenderMode(GL_FEEDBACK);
132       (*display)();
133       GLint returned = glRenderMode(GL_RENDER);
134       if(returned > 0){
135 	feedbackBuffer.resize(returned);
136 	break;
137       }
138       bufferSize += 100*1024;
139       feedbackBuffer.resize(bufferSize);
140     }
141 
142 
143     GLfloat clearColor[4], viewport[4];
144     GLfloat lineWidth;
145     GLfloat pointSize;
146 
147     // Read back a bunch of OpenGL state to help make the EPS
148     // consistent with the OpenGL clear color, line width, point
149     // size, and viewport.
150     glGetFloatv(GL_VIEWPORT, viewport);
151     glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor);
152     glGetFloatv(GL_LINE_WIDTH, &lineWidth);
153     glGetFloatv(GL_POINT_SIZE, &pointSize);
154 
155     // Emit EPS header.
156     output << "%!PS-Adobe-2.0 EPSF-2.0\n";
157     // Notice %% for a single % in the fprintf calls.
158     output << "%%Creator: ProtoMol (using OpenGL feedback)\n";
159     output << "%%BoundingBox: " << viewport[0] << " " << viewport[1] << " " << viewport[2] << " " << viewport[3] << "\n";
160     output << "%%EndComments\n";
161     output << "\n";
162     output << "gsave\n";
163     output << "\n";
164 
165     // Output Frederic Delhoume's "gouraudtriangle" PostScript fragment.
166     output << "% the gouraudtriangle PostScript fragement below is free\n";
167     output << "% written by Frederic Delhoume (delhoume@ilog.fr)\n";
168     output << "/threshold " << Constant::EPS_GOURAUD_THRESHOLD << " def\n"
169 	   << "/bd{bind def}bind def /triangle { aload pop   setrgbcolor  aload pop 5 3\n"
170 	   << "roll 4 2 roll 3 2 roll exch moveto lineto lineto closepath fill } bd\n"
171 	   << "/computediff1 { 2 copy sub abs threshold ge {pop pop pop true} { exch 2\n"
172 	   << "index sub abs threshold ge { pop pop true} { sub abs threshold ge } ifelse\n"
173 	   << "} ifelse } bd /computediff3 { 3 copy 0 get 3 1 roll 0 get 3 1 roll 0 get\n"
174 	   << "computediff1 {true} { 3 copy 1 get 3 1 roll 1 get 3 1 roll 1 get\n"
175 	   << "computediff1 {true} { 3 copy 2 get 3 1 roll  2 get 3 1 roll 2 get\n"
176 	   << "computediff1 } ifelse } ifelse } bd /middlecolor { aload pop 4 -1 roll\n"
177 	   << "aload pop 4 -1 roll add 2 div 5 1 roll 3 -1 roll add 2 div 3 1 roll add 2\n"
178 	   << "div 3 1 roll exch 3 array astore } bd /gouraudtriangle { computediff3 { 4\n"
179 	   << "-1 roll aload 7 1 roll 6 -1 roll pop 3 -1 roll pop add 2 div 3 1 roll add\n"
180 	   << "2 div exch 3 -1 roll aload 7 1 roll exch pop 4 -1 roll pop add 2 div 3 1\n"
181 	   << "roll add 2 div exch 3 -1 roll aload 7 1 roll pop 3 -1 roll pop add 2 div 3\n"
182 	   << "1 roll add 2 div exch 7 3 roll 10 -3 roll dup 3 index middlecolor 4 1 roll\n"
183 	   << "2 copy middlecolor 4 1 roll 3 copy pop middlecolor 4 1 roll 13 -1 roll\n"
184 	   << "aload pop 17 index 6 index 15 index 19 index 6 index 17 index 6 array\n"
185 	   << "astore 10 index 10 index 14 index gouraudtriangle 17 index 5 index 17\n"
186 	   << "index 19 index 5 index 19 index 6 array astore 10 index 9 index 13 index\n"
187 	   << "gouraudtriangle 13 index 16 index 5 index 15 index 18 index 5 index 6\n"
188 	   << "array astore 12 index 12 index 9 index gouraudtriangle 17 index 16 index\n"
189 	   << "15 index 19 index 18 index 17 index 6 array astore 10 index 12 index 14\n"
190 	   << "index gouraudtriangle 18 {pop} repeat } { aload pop 5 3 roll aload pop 7 3\n"
191 	   << "roll aload pop 9 3 roll 4 index 6 index 4 index add add 3 div 10 1 roll 7\n"
192 	   << "index 5 index 3 index add add 3 div 10 1 roll 6 index 4 index 2 index add\n"
193 	   << "add 3 div 10 1 roll 9 {pop} repeat 3 array astore triangle } ifelse } bd";
194 
195 
196 
197     output << "\n" << lineWidth << " setlinewidth\n";
198 
199     // Clear the background like OpenGL had it.
200     output << clearColor[0] << " " << clearColor[1] << " " << clearColor[2] << " setrgbcolor\n";
201     output << viewport[0] << " " << viewport[1] << " " << viewport[2] << " " << viewport[3] << " rectfill\n\n";
202 
203 
204     // Collect all primitives
205     Feedback3Dcolor *vertex;
206     vector<DepthIndex> prims;
207     GLfloat* loc = &(feedbackBuffer[0]);
208     GLfloat* end = loc + feedbackBuffer.size();
209     while (loc < end) {
210       DepthIndex item;
211       double depthSum;
212       int nvertices;
213       double area;
214 
215       item.ptr = loc;  // Save this primitive's location.
216       int token = static_cast<int>(*loc);
217       loc++;
218       count++;
219       switch (token) {
220       case GL_LINE_TOKEN:
221       case GL_LINE_RESET_TOKEN:
222 	vertex = (Feedback3Dcolor*) loc;
223 	depthSum = vertex[0].z + vertex[1].z;
224 	item.depth = depthSum / 2.0;
225 	prims.push_back(item);
226 	loc += 14;
227 	break;
228       case GL_POLYGON_TOKEN:
229 	nvertices = static_cast<int>(*loc);
230 	loc++;
231 	vertex = (Feedback3Dcolor*) loc;
232 	depthSum = vertex[0].z;
233 	area = 0.0;
234 	for(int i = 1; i < nvertices; i++) {
235 	  depthSum += vertex[i].z;
236 	  area += (vertex[0].y- vertex[i-1].y)*(vertex[i].x-vertex[i-1].x)-(vertex[0].x- vertex[i-1].x)*(vertex[i].y-vertex[i-1].y);
237 	}
238 	item.depth = depthSum / nvertices;
239 	// Only add if triangle is visible and not degenerated
240 	if(area > 0.04)
241 	  prims.push_back(item);
242 	loc += (7 * nvertices);
243 	break;
244       case GL_POINT_TOKEN:
245 	vertex = (Feedback3Dcolor*) loc;
246 	item.depth = vertex[0].z;
247 	prims.push_back(item);
248 	loc += 7;
249 	break;
250       default:
251 	report << recoverable << token << " - opps a GL token I do not understand."<<endr;
252 
253 	break;
254       }
255     }
256 
257     // Sort them
258     sort(prims.begin(),prims.end());
259 
260     // Print them
261     for(unsigned int j=0;j<prims.size();j++){
262       GLfloat* loc = prims[j].ptr;
263 
264       GLfloat red = 0.0, green = 0.0, blue = 0.0;
265       GLfloat dx = 0.0, dy = 0.0, dr = 0.0, dg = 0.0, db = 0.0, absR = 0.0, absG = 0.0, absB = 0.0, colormax = 0.0;
266       GLfloat xstep = 0.0, ystep = 0.0, rstep = 0.0, gstep = 0.0, bstep = 0.0;
267       GLfloat xnext = 0.0, ynext = 0.0, rnext = 0.0, gnext = 0.0, bnext = 0.0, distance = 0.0;
268       int steps,nvertices;
269 
270       int token = static_cast<int>(*loc);
271       loc++;
272       switch (token) {
273       case GL_LINE_RESET_TOKEN:
274       case GL_LINE_TOKEN:
275 	vertex = (Feedback3Dcolor*) loc;
276 
277 	dr = vertex[1].red - vertex[0].red;
278 	dg = vertex[1].green - vertex[0].green;
279 	db = vertex[1].blue - vertex[0].blue;
280 
281 	if (dr != 0 || dg != 0 || db != 0) {
282 	  // Smooth shaded line.
283 	  dx = vertex[1].x - vertex[0].x;
284 	  dy = vertex[1].y - vertex[0].y;
285 	  distance = sqrt(dx * dx + dy * dy);
286 
287 	  absR = fabs(dr);
288 	  absG = fabs(dg);
289 	  absB = fabs(db);
290 
291 
292 	  colormax = std::max(absR, std::max(absG, absB));
293 	  steps = static_cast<int>(std::max(1.0, static_cast<double>(colormax * distance) * Constant::EPS_SMOOTH_LINE_FACTOR));
294 
295 	  xstep = dx / steps;
296 	  ystep = dy / steps;
297 
298 	  rstep = dr / steps;
299 	  gstep = dg / steps;
300 	  bstep = db / steps;
301 
302 	  xnext = vertex[0].x;
303 	  ynext = vertex[0].y;
304 	  rnext = vertex[0].red;
305 	  gnext = vertex[0].green;
306 	  bnext = vertex[0].blue;
307 
308 	  // Back up half a step; we want the end points to be
309 	  //   exactly the their endpoint colors.
310 	  xnext -= xstep / 2.0;
311 	  ynext -= ystep / 2.0;
312 	  rnext -= rstep / 2.0;
313 	  gnext -= gstep / 2.0;
314 	  bnext -= bstep / 2.0;
315 	} else {
316 	  // Single color line.
317 	  steps = 0;
318 	}
319 
320 	output << vertex[0].red << " " << vertex[0].green << " " << vertex[0].blue << " setrgbcolor\n";
321 	output << vertex[0].x << " " << vertex[0].y << " moveto\n";
322 
323 	for(int i = 0; i < steps; i++) {
324 	  xnext += xstep;
325 	  ynext += ystep;
326 	  rnext += rstep;
327 	  gnext += gstep;
328 	  bnext += bstep;
329 	  output << xnext << " " << ynext << "  lineto stroke\n";
330 	  output << rnext << " " << gnext << " " << bnext << "   setrgbcolor\n";
331 	  output << xnext << " " << ynext << "  moveto\n";
332 	}
333 	output << vertex[1].x << " " << vertex[1].y << "  lineto stroke\n";
334 
335 	loc += 14;          // Each vertex element in the feedback
336 	//   buffer is 7 GLfloats.
337 
338 	break;
339       case GL_POLYGON_TOKEN:
340 	nvertices = static_cast<int>(*loc);
341 	loc++;
342 
343 	vertex = (Feedback3Dcolor*) loc;
344 
345 
346 	if (nvertices > 0) {
347 	  red = vertex[0].red;
348 	  green = vertex[0].green;
349 	  blue = vertex[0].blue;
350 	  bool smooth = (vertex[0].alpha >= 1.0);
351 	  for(int i = 1; i < nvertices; i++) {
352 	    if (vertex[0].alpha < 1.0 || red != vertex[i].red || green != vertex[i].green || blue != vertex[i].blue) {
353 	      smooth = true;
354 	      break;
355 	    }
356 	  }
357 
358 	  if (smooth) {
359 	    // Smooth shaded polygon; varying colors at vetices.
360 	    //int triOffset;
361 
362 	    // Break polygon into "nvertices-2" triangle fans.
363 	    for(int i = 0; i < nvertices - 2; i++) {
364 	      //triOffset = i * 7;
365 	      output << "[ " << vertex[0].x << " " << vertex[i + 1].x << " " << vertex[i + 2].x << " "
366 		     << vertex[0].y << " " << vertex[i + 1].y << " " << vertex[i + 2].y << " ] ";
367 	      output << "[ " << vertex[0].red << " " << vertex[0].green << " " << vertex[0].blue << " ] "
368 		     << "[ " << vertex[i + 1].red << " " << vertex[i + 1].green << " " << vertex[i + 1].blue << " ] "
369 		     << "[ " << vertex[i + 2].red << " " << vertex[i + 2].green << " " << vertex[i + 2].blue << " ] gouraudtriangle\n";
370 	    }
371 	  } else {
372 	    // Flat shaded polygon; all vertex colors the same.
373 	    output << "newpath\n";
374 	    output << red << " " << green << " " << blue << " setrgbcolor\n";
375 
376 	    // Draw a filled triangle.
377 	    output << vertex[0].x << " " << vertex[0].y << " moveto\n";
378 	    for(int i = 1; i < nvertices; i++) {
379 	      output << vertex[i].x << " " << vertex[i].y << " lineto\n";
380 	    }
381 	    output << "closepath fill\n\n";
382 	  }
383 	}
384 	loc += nvertices * 7;  // Each vertex element in the
385 	//   feedback buffer is 7 GLfloats.
386 	break;
387       case GL_POINT_TOKEN:
388 	vertex = (Feedback3Dcolor*) loc;
389 	output << vertex[0].red << " " << vertex[0].green << " " << vertex[0].blue << " setrgbcolor\n";
390 	output << vertex[0].x << " " << vertex[0].y << " " << pointSize / 2.0 << " 0 360 arc fill\n\n";
391 	loc += 7;           // Each vertex element in the feedback
392 	//   buffer is 7 GLfloats.
393 	break;
394       default:
395 	report << recoverable << token << " - opps a GL token I do not understand"<<endr;
396 	break;
397       }
398 
399 
400     }
401 
402 
403 
404     // Emit EPS trailer.
405     output << "grestore\n\n";
406     //output << "showpage\n%% stop using temporary dictionary\nend\n\n%% restore original state\norigstate restore\n\n%%Trailer\n\n";
407     //output << "%Add `showpage' to the end of this file to be able to print to a printer.\n";
408     output << "showpage\n\n";
409 #else
410     output << "%!PS-Adobe-2.0 EPSF-2.0\n";
411     output << "%%Title: nosupport.eps\n";
412     output << "%%Creator: ProtoMol\n";
413     output << "%%CreationDate: Jan  1 00:00:00 1970\n";
414     output << "%%For: ProtoMol\n";
415     output << "%%BoundingBox: 0 0 165 12\n";
416     output << "%%Magnification: 1.0000\n";
417     output << "%%EndComments\n";
418     output << "/$F2psDict 200 dict def\n";
419     output << "$F2psDict begin\n";
420     output << "$F2psDict /mtrx matrix put\n";
421     output << "end\n";
422     output << "save\n";
423     output << "newpath 0 12 moveto 0 0 lineto 165 0 lineto 165 12 lineto closepath clip newpath\n";
424     output << "-63.0 88.7 translate\n";
425     output << "1 -1 scale\n";
426     output << "10 setmiterlimit\n";
427     output << " 0.06000 0.06000 scale\n";
428     output << "/Times-Roman findfont 180.00 scalefont setfont\n";
429     output << "1050 1425 moveto\n";
430     output << "gsave 1 -1 scale (Compiled without OpenGL support!) 0.000 0.000 0.000 setrgbcolor show grestore\n";
431     output << "restore\n";
432 #endif
433     return count;
434 
435   }
436 
437   //_____________________________________________________________________ openglToPPM
openglToPPM(PPM & ppm)438   void openglToPPM(PPM& ppm){
439 
440 #if defined(HAVE_OPENGL) || defined(HAVE_GLUT)
441     GLint viewport[4]; // -> x, y, width, height
442     glGetIntegerv(GL_VIEWPORT, viewport);
443     GLint width  = viewport[2]-viewport[0];
444     GLint height = viewport[3]-viewport[1];
445 
446     long size = width*height*3;
447     GLfloat* pixels = new GLfloat[size];
448     GLint drawBuffer;
449     glGetIntegerv(GL_DRAW_BUFFER, &drawBuffer);
450     glReadBuffer((GLenum)drawBuffer);  // read from correct buffer
451     glReadPixels(viewport[0], viewport[1], width, height, GL_RGB, GL_FLOAT, pixels);
452 
453     ppm.resize(width,height);
454 
455     GLfloat* p = pixels;
456     for(int j=0;j<height;j++){
457       for(int i=0;i<width;i++){
458 	unsigned char c[3];
459 	for(unsigned int k=0;k<3;k++){
460 	  if((*p) <= 0.0){
461 	    c[k] = 0U;
462 	  }
463 	  else 	if((*p) >= 1.0){
464 	    c[k] = 255U;
465 	  }
466 	  else {
467 	    c[k] = static_cast<unsigned char>((*p)*255);
468 	  }
469 	  p++;
470 	}
471 	ppm.set(i,j,c[0],c[1],c[2]);
472       }
473     }
474     delete [] pixels;
475 #else
476     ppm.resize(0,0);
477 #endif
478 
479 
480   }
481 
482   //_____________________________________________________________________ openglToPGM
openglToPGM(PGM & pgm)483   void openglToPGM(PGM& pgm){
484 
485 #if defined(HAVE_OPENGL) || defined(HAVE_GLUT)
486     GLint viewport[4]; // -> x, y, width, height
487     glGetIntegerv(GL_VIEWPORT, viewport);
488     GLint width  = viewport[2]-viewport[0];
489     GLint height = viewport[3]-viewport[1];
490 
491     long size = width*height*3;
492     GLfloat* pixels = new GLfloat[size];
493     GLint drawBuffer;
494     glGetIntegerv(GL_DRAW_BUFFER, &drawBuffer);
495     glReadBuffer((GLenum)drawBuffer);  // read from correct buffer
496     glReadPixels(viewport[0], viewport[1], width, height, GL_RGB, GL_FLOAT, pixels);
497 
498     pgm.resize(width,height);
499 
500     GLfloat* p = pixels;
501     for(int j=0;j<height;j++){
502       for(int i=0;i<width;i++){
503 	int c = 0;
504 	for(unsigned int k=0;k<3;k++){
505 	  if((*p) >= 1.0){
506 	    c += 255;
507 	  }
508 	  else 	if((*p) > 0.0){
509 	    c += (*p)*255;
510 	  }
511 	  p++;
512 	}
513 	pgm.set(i,j,static_cast<unsigned char>(c/3));
514       }
515     }
516     delete [] pixels;
517 #else
518     pgm.resize(0,0);
519 #endif
520 
521 
522   }
523 
524 
525 }
526