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