Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

PsRenderer.cpp

Go to the documentation of this file.
00001 
00002 /*
00003     TEDDY - General graphics application library
00004     Copyright (C) 1999-2002  Timo Suoranta
00005     tksuoran@cc.helsinki.fi
00006 
00007         Adapted from
00008 
00009         Copyright (c) Mark J. Kilgard, 1997.
00010 
00011         This program is freely distributable without licensing fees
00012         and is provided without guarantee or warrantee expressed or
00013         implied. This program is -not- in the public domain.
00014 
00015         Example showing how to use OpenGL's feedback mode to capture
00016         transformed vertices and output them as Encapsulated PostScript.
00017         Handles limited hidden surface removal by sorting and does
00018         smooth shading (albeit limited due to PostScript).
00019 
00020     This library is free software; you can redistribute it and/or
00021     modify it under the terms of the GNU Lesser General Public
00022     License as published by the Free Software Foundation; either
00023     version 2.1 of the License, or (at your option) any later version.
00024 
00025     This library is distributed in the hope that it will be useful,
00026     but WITHOUT ANY WARRANTY; without even the implied warranty of
00027     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00028     Lesser General Public License for more details.
00029 
00030     You should have received a copy of the GNU Lesser General Public
00031     License along with this library; if not, write to the Free Software
00032     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00033 
00034     $Id:  $
00035 */
00036 
00037 
00038 #include "Teddy/Graphics/PsRenderer.h"
00039 #include "Teddy/SysSupport/StdMaths.h"
00040 #include <cassert>
00041 #include <algorithm>
00042 using namespace Teddy::SysSupport;
00043 
00044 
00045 namespace Teddy    {
00046 namespace Graphics {
00047 
00048 
00049 #define EPS_SMOOTH_LINE_FACTOR 0.06
00050 #define EPS_GOURAUD_THRESHOLD  0.05
00051 #define EPS_LINE_WIDTH         0.1
00052 
00053 
00054 //  OpenGL's GL_3D_COLOR feedback vertex format.
00055 struct Feedback3Dcolor {
00056     GLfloat x;
00057     GLfloat y;
00058     GLfloat z;
00059     GLfloat red;
00060     GLfloat green;
00061     GLfloat blue;
00062     GLfloat alpha;
00063 };
00064 
00065 
00066 static char *NicegouraudtriangleEPS[] = {
00067     "\n% Smooth-shaded triangle - x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 ST",
00068     "/ST {",
00069     "   /b1 exch def",
00070     "   /g1 exch def",
00071     "   /r1 exch def",
00072     "   /y1 exch def",
00073     "   /x1 exch def",
00074     "   /b2 exch def",
00075     "   /g2 exch def",
00076     "   /r2 exch def",
00077     "   /y2 exch def",
00078     "   /x2 exch def",
00079     "   /b3 exch def",
00080     "   /g3 exch def",
00081     "   /r3 exch def",
00082     "   /y3 exch def",
00083     "   /x3 exch def",
00084     "   b2 b1 sub abs 0.05 gt",
00085     "   g2 g1 sub abs 0.017 gt",
00086     "   r2 r1 sub abs 0.032 gt",
00087     "   b3 b1 sub abs 0.05 gt",
00088     "   g3 g1 sub abs 0.017 gt",
00089     "   r3 r1 sub abs 0.032 gt",
00090     "   b2 b3 sub abs 0.05 gt",
00091     "   g2 g3 sub abs 0.017 gt",
00092     "   r2 r3 sub abs 0.032 gt",
00093     "   or or or or or or or or {",
00094     "       /b12 b1 b2 add 0.5 mul def",
00095     "       /g12 g1 g2 add 0.5 mul def",
00096     "       /r12 r1 r2 add 0.5 mul def",
00097     "       /y12 y1 y2 add 0.5 mul def",
00098     "       /x12 x1 x2 add 0.5 mul def",
00099     "       /b13 b1 b3 add 0.5 mul def",
00100     "       /g13 g1 g3 add 0.5 mul def",
00101     "       /r13 r1 r3 add 0.5 mul def",
00102     "       /y13 y1 y3 add 0.5 mul def",
00103     "       /x13 x1 x3 add 0.5 mul def",
00104     "       /b32 b3 b2 add 0.5 mul def",
00105     "       /g32 g3 g2 add 0.5 mul def",
00106     "       /r32 r3 r2 add 0.5 mul def",
00107     "       /y32 y3 y2 add 0.5 mul def",
00108     "       /x32 x3 x2 add 0.5 mul def",
00109     "       x1 y1 r1 g1 b1 x12 y12 r12 g12 b12 x13 y13 r13 g13 b13",
00110     "       x2 y2 r2 g2 b2 x12 y12 r12 g12 b12 x32 y32 r32 g32 b32",
00111     "       x3 y3 r3 g3 b3 x32 y32 r32 g32 b32 x13 y13 r13 g13 b13",
00112     "       x32 y32 r32 g32 b32 x12 y12 r12 g12 b12 x13 y13 r13 g13 b13",
00113     "       ST ST ST ST",
00114     "   } {",
00115     "       x1 y1 x2 y2 x3 y3 r1 g1 b1 T",
00116     "   } ifelse",
00117     "} bind def"
00118     "\n",
00119     NULL
00120 };
00121 
00122 
00123 static char *gouraudtriangleEPS[] ={
00124     "/bd{bind def}bind def /triangle { aload pop   setrgbcolor  aload pop 5 3",
00125     "roll 4 2 roll 3 2 roll exch moveto lineto lineto closepath fill } bd",
00126     "/computediff1 { 2 copy sub abs threshold ge {pop pop pop true} { exch 2",
00127     "index sub abs threshold ge { pop pop true} { sub abs threshold ge } ifelse",
00128     "} ifelse } bd /computediff3 { 3 copy 0 get 3 1 roll 0 get 3 1 roll 0 get",
00129     "computediff1 {true} { 3 copy 1 get 3 1 roll 1 get 3 1 roll 1 get",
00130     "computediff1 {true} { 3 copy 2 get 3 1 roll  2 get 3 1 roll 2 get",
00131     "computediff1 } ifelse } ifelse } bd /middlecolor { aload pop 4 -1 roll",
00132     "aload pop 4 -1 roll add 2 div 5 1 roll 3 -1 roll add 2 div 3 1 roll add 2",
00133     "div 3 1 roll exch 3 array astore } bd /gouraudtriangle { computediff3 { 4",
00134     "-1 roll aload 7 1 roll 6 -1 roll pop 3 -1 roll pop add 2 div 3 1 roll add",
00135     "2 div exch 3 -1 roll aload 7 1 roll exch pop 4 -1 roll pop add 2 div 3 1",
00136     "roll add 2 div exch 3 -1 roll aload 7 1 roll pop 3 -1 roll pop add 2 div 3",
00137     "1 roll add 2 div exch 7 3 roll 10 -3 roll dup 3 index middlecolor 4 1 roll",
00138     "2 copy middlecolor 4 1 roll 3 copy pop middlecolor 4 1 roll 13 -1 roll",
00139     "aload pop 17 index 6 index 15 index 19 index 6 index 17 index 6 array",
00140     "astore 10 index 10 index 14 index gouraudtriangle 17 index 5 index 17",
00141     "index 19 index 5 index 19 index 6 array astore 10 index 9 index 13 index",
00142     "gouraudtriangle 13 index 16 index 5 index 15 index 18 index 5 index 6",
00143     "array astore 12 index 12 index 9 index gouraudtriangle 17 index 16 index",
00144     "15 index 19 index 18 index 17 index 6 array astore 10 index 12 index 14",
00145     "index gouraudtriangle 18 {pop} repeat } { aload pop 5 3 roll aload pop 7 3",
00146     "roll aload pop 9 3 roll 8 index 6 index 4 index add add 3 div 10 1 roll 7",
00147     "index 5 index 3 index add add 3 div 10 1 roll 6 index 4 index 2 index add",
00148     "add 3 div 10 1 roll 9 {pop} repeat 3 array astore triangle } ifelse } bd",
00149     NULL
00150 };
00151 
00152 
00153 
00154 void PsRenderer::writePs( char *pFilename, GLfloat *pFeedbackBuffer, int NbValues, bool sort ){
00155     assert( pFilename );
00156 
00157     FILE *pFile = fopen( pFilename, "wt" );
00158     if( !pFile ){
00159         return;
00160     }
00161 
00162     spewWireFrameEPS( pFile, sort, NbValues, pFeedbackBuffer );
00163     fclose( pFile );
00164 }
00165 
00166 
00167 GLfloat *PsRenderer::spewPrimitiveEPS( FILE *file, GLfloat *loc ){
00168     int              token;
00169     int              nvertices, i;
00170     GLfloat          red, green, blue;
00171     int              smooth;
00172     GLfloat          dx, dy, dr, dg, db, absR, absG, absB, colormax;
00173     int              steps;
00174     Feedback3Dcolor *vertex;
00175     GLfloat          xstep, ystep, rstep, gstep, bstep;
00176     GLfloat          xnext, ynext, rnext, gnext, bnext, distance;
00177 
00178     token = (int)*loc;
00179     loc++;
00180     switch( token ){
00181     case GL_LINE_RESET_TOKEN:
00182     case GL_LINE_TOKEN:
00183         vertex = (Feedback3Dcolor *) loc;
00184 
00185         dr = vertex[1].red   - vertex[0].red;
00186         dg = vertex[1].green - vertex[0].green;
00187         db = vertex[1].blue  - vertex[0].blue;
00188 
00189         if( dr != 0 || dg != 0 || db != 0 ){
00190             //  Smooth shaded line.
00191             dx       = vertex[1].x - vertex[0].x;
00192             dy       = vertex[1].y - vertex[0].y;
00193             distance = (float)sqrt(dx * dx + dy * dy);
00194 
00195             absR = (float)fabs( dr );
00196             absG = (float)fabs( dg );
00197             absB = (float)fabs( db );
00198 
00199             colormax = MAX( absR, MAX(absG, absB) );
00200             steps = (int)MAX( 1.0, colormax * distance * EPS_SMOOTH_LINE_FACTOR );
00201 
00202             xstep = dx / steps;
00203             ystep = dy / steps;
00204 
00205             rstep = dr / steps;
00206             gstep = dg / steps;
00207             bstep = db / steps;
00208 
00209             xnext = vertex[0].x;
00210             ynext = vertex[0].y;
00211             rnext = vertex[0].red;
00212             gnext = vertex[0].green;
00213             bnext = vertex[0].blue;
00214 
00215             /* Back up half a step; we want the end points to be
00216              exactly the their endpoint colors. */
00217             xnext -= (float)xstep / 2.0f;
00218             ynext -= (float)ystep / 2.0f;
00219             rnext -= (float)rstep / 2.0f;
00220             gnext -= (float)gstep / 2.0f;
00221             bnext -= (float)bstep / 2.0f;
00222 
00223             fprintf( file, "%g %g %g setrgbcolor\n", vertex[0].red, vertex[0].green, vertex[0].blue );
00224             fprintf( file, "%g %g moveto\n",         vertex[0].x,   vertex[0].y );
00225 
00226             for( i = 0; i < steps; i++ ){
00227                 xnext += xstep;
00228                 ynext += ystep;
00229                 rnext += rstep;
00230                 gnext += gstep;
00231                 bnext += bstep;
00232                 fprintf( file, "%g %g lineto stroke\n", xnext, ynext );
00233                 fprintf( file, "%g %g %g setrgbcolor\n", rnext, gnext, bnext );
00234                 fprintf( file, "%g %g moveto\n", xnext, ynext);
00235             }
00236             fprintf( file, "%g %g lineto stroke\n", vertex[1].x, vertex[1].y );
00237         }else{  //  single color segment -> S
00238             fprintf( file, "%g %g %g %g %g %g %g S\n",vertex[1].x,vertex[1].y, vertex[0].x,vertex[0].y,vertex[0].red, vertex[0].green, vertex[0].blue );
00239         }
00240 
00241         loc += 14;  /* Each vertex element in the feedback buffer is 7 GLfloats. */
00242 
00243         break;
00244 
00245     case GL_POLYGON_TOKEN:
00246         nvertices = (int)*loc;
00247         loc++;
00248 
00249         vertex = (Feedback3Dcolor *) loc;
00250 
00251         if( nvertices > 0 ){
00252             red    = vertex[0].red;
00253             green  = vertex[0].green;
00254             blue   = vertex[0].blue;
00255             smooth = 0;
00256             for( i=1; i<nvertices; i++ ){
00257                 if (red   != vertex[i].red || green != vertex[i].green || blue  != vertex[i].blue ){
00258                     smooth = 1;
00259                     break;
00260                 }
00261             }
00262             if( smooth ){
00263                 /* Smooth shaded polygon; varying colors at vertices. */
00264                 int triOffset;
00265 
00266                 /* Break polygon into "nvertices-2" triangle fans. */
00267                 for( i=0; i<nvertices - 2; i++ ){
00268                     triOffset = i * 7;
00269 
00270                     fprintf(file, "[%g %g %g %g %g %g]", vertex[0].x, vertex[i + 1].x, vertex[i + 2].x, vertex[0].y, vertex[i + 1].y, vertex[i + 2].y );
00271                     fprintf(file, " [%g %g %g] [%g %g %g] [%g %g %g] gouraudtriangle\n", vertex[0].red, vertex[0].green, vertex[0].blue, vertex[i + 1].red, vertex[i + 1].green, vertex[i + 1].blue, vertex[i + 2].red, vertex[i + 2].green, vertex[i + 2].blue );
00272                     /*
00273                      // x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 ST
00274                      fprintf(file,"%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g ST\n",
00275                      vertex[i+2].x,vertex[i+2].y,vertex[i+2].red,vertex[i+2].green,vertex[i+2].blue,
00276                      vertex[i+1].x,vertex[i+1].y,vertex[i+1].red,vertex[i+1].green,vertex[i+1].blue,
00277                      vertex[0].x,vertex[0].y,vertex[0].red,vertex[0].green,vertex[0].blue);*/
00278                 }
00279             }else{
00280                 if( nvertices > 3 ){ // polygon
00281                     // Draw a filled polygon
00282                     fprintf( file, "newpath\n" );
00283                     fprintf( file, "%g %g %g setrgbcolor\n", red, green, blue );
00284                     fprintf( file, "%g %g moveto\n", vertex[0].x, vertex[0].y);
00285                     for( i=1; i<nvertices; i++){
00286                         fprintf( file, "%g %g lineto\n", vertex[i].x, vertex[i].y );
00287                     }
00288                     fprintf( file, "closepath fill\n\n" );
00289                 }else{ // triangle -> T
00290                     fprintf(file,"%g %g %g %g %g %g %g %g %g T\n", vertex[2].x,vertex[2].y, vertex[1].x,vertex[1].y, vertex[0].x,vertex[0].y, red,green,blue );
00291                 }
00292             }
00293         }
00294         loc += nvertices * 7;  /* Each vertex element in the feedback buffer is 7 GLfloats. */
00295         break;
00296 
00297     case GL_POINT_TOKEN:
00298         vertex = (Feedback3Dcolor *) loc;
00299         fprintf(file, "%g %g %g 0 360 %g %g %g P\n",vertex[0].x, vertex[0].y, m_PointSize / 2.0,vertex[0].red,vertex[0].green,vertex[0].blue );
00300         loc += 7;           /* Each vertex element in the feedback buffer is 7 GLfloats. */
00301         break;
00302     default:
00303         /* XXX Left as an excersie to the reader. */
00304         //printf("Incomplete implementation.  Unexpected token (%d).\n", token);
00305         //exit(1);
00306         break;
00307     }
00308     return loc;
00309 }
00310 
00311 void PsRenderer::spewUnsortedFeedback( FILE *file, GLint size, GLfloat * buffer ){
00312     GLfloat *loc, *end;
00313 
00314     loc = buffer;
00315     end = buffer + size;
00316     while( loc < end ){
00317         loc = spewPrimitiveEPS( file, loc );
00318     }
00319 }
00320 
00321 
00322 int PsRenderer::compare( const void *a, const void *b ){
00323     DepthIndex *p1   = (DepthIndex *) a;
00324     DepthIndex *p2   = (DepthIndex *) b;
00325     GLfloat     diff = p2->depth - p1->depth;
00326 
00327     if( diff > 0.0 ){
00328         return 1;
00329     }else if( diff < 0.0 ){
00330         return -1;
00331     }else{
00332         return 0;
00333     }
00334 }
00335 
00336 
00337 void PsRenderer::spewSortedFeedback( FILE *file, GLint size, GLfloat *buffer ){
00338     int              token;
00339     GLfloat         *loc, *end;
00340     Feedback3Dcolor *vertex;
00341     GLfloat          depthSum;
00342     int              nprimitives, item;
00343     DepthIndex      *prims;
00344     int              nvertices, i;
00345 
00346     end = buffer + size;
00347 
00348     /* Count how many primitives there are. */
00349     nprimitives = 0;
00350     loc = buffer;
00351     while( loc < end ){
00352         token = (int)*loc;
00353         loc++;
00354         switch( token ){
00355         case GL_LINE_TOKEN:
00356         case GL_LINE_RESET_TOKEN:
00357             loc += 14;
00358             nprimitives++;
00359             break;
00360         case GL_POLYGON_TOKEN:
00361             nvertices = (int)*loc;
00362             loc++;
00363             loc += (7 * nvertices);
00364             nprimitives++;
00365             break;
00366         case GL_POINT_TOKEN:
00367             loc += 7;
00368             nprimitives++;
00369             break;
00370         default:
00371             //  XXX Left as an excersie to the reader.
00372             //printf("Incomplete implementation.  Unexpected token (%d).\n", token);
00373             //exit(1);
00374             break;
00375         }
00376     }
00377 
00378     //  Allocate an array of pointers that will point back at
00379     //  primitives in the feedback buffer.  There will be one
00380     //  entry per primitive.  This array is also where we keep the
00381     //  primitive's average depth.  There is one entry per
00382     //  primitive  in the feedback buffer.
00383     prims = new DepthIndex[nprimitives];
00384 
00385     item = 0;
00386     loc  = buffer;
00387     while( loc < end ){
00388         prims[item].ptr = loc;  //  Save this primitive's location.
00389         token = (int)*loc;
00390         loc++;
00391         switch( token ){
00392         case GL_LINE_TOKEN:
00393         case GL_LINE_RESET_TOKEN:
00394             vertex   = (Feedback3Dcolor *) loc;
00395             depthSum = vertex[0].z + vertex[1].z;
00396             prims[item].depth = (float)depthSum / 2.0f;
00397             loc += 14;
00398             break;
00399         case GL_POLYGON_TOKEN:
00400             nvertices = (int)*loc;
00401             loc++;
00402             vertex = (Feedback3Dcolor *) loc;
00403             depthSum = vertex[0].z;
00404             for( i=1; i<nvertices; i++ ){
00405                 depthSum += vertex[i].z;
00406             }
00407             prims[item].depth = depthSum / nvertices;
00408             loc += (7 * nvertices);
00409             break;
00410         case GL_POINT_TOKEN:
00411             vertex = (Feedback3Dcolor *) loc;
00412             prims[item].depth = vertex[0].z;
00413             loc += 7;
00414             break;
00415         default:
00416             //  XXX Left as an excersie to the reader. */
00417             //  ASSERT(1);
00418             break;
00419         }
00420         item++;
00421     }
00422     assert( item == nprimitives );
00423 
00424     //  Sort the primitives back to front.
00425     qsort( prims, nprimitives, sizeof(DepthIndex), compare );
00426 
00427     //  XXX Understand that sorting by a primitives average depth
00428     //  doesn't allow us to disambiguate some cases like self
00429     //  intersecting polygons.  Handling these cases would require
00430     //  breaking up the primitives.  That's too involved for this
00431     //  example.  Sorting by depth is good enough for lots of
00432     //  applications.
00433 
00434     //  Emit the Encapsulated PostScript for the primitives in back to front order.
00435     for( item=0; item<nprimitives; item++ ){
00436         (void) spewPrimitiveEPS( file, prims[item].ptr );
00437     }
00438 
00439     delete[] prims;
00440 }
00441 
00442 
00443 void PsRenderer::spewWireFrameEPS( FILE *file, bool doSort, GLint size, GLfloat *buffer ){
00444     GLfloat clearColor[4], viewport[4];
00445     GLfloat lineWidth;
00446 
00447     //  Read back a bunch of OpenGL state to help make the EPS
00448     //  consistent with the OpenGL clear color, line width, point
00449     //  size, and viewport.
00450     glGetFloatv( GL_VIEWPORT,           viewport    );
00451     glGetFloatv( GL_COLOR_CLEAR_VALUE,  clearColor  );
00452     glGetFloatv( GL_LINE_WIDTH,        &lineWidth   );
00453     glGetFloatv( GL_POINT_SIZE,        &m_PointSize );
00454 
00455     //  Emit EPS header.
00456     fputs( "%!PS-Adobe-2.0 EPSF-2.0\n", file );
00457     //  Notice %% for a single % in the fprintf calls.
00458     fprintf( file, "%%%%BoundingBox: %g %g %g %g\n", viewport[0], viewport[1], viewport[2], viewport[3] );
00459     fputs( "%%EndComments\n", file );
00460     fputs( "\n", file );
00461     fputs( "gsave\n", file );
00462     fputs( "\n", file );
00463 
00464     fprintf( file, "/threshold %g def\n", EPS_GOURAUD_THRESHOLD );
00465 
00466     fprintf( file, "\n%% RGB color command - r g b C\n" );
00467     fprintf( file, "/C { setrgbcolor } bind def\n" );
00468 
00469     //  This is more compact
00470     fprintf( file, "\n%% Point - x_center y_center PointSize/2 0 360 r g b P\n" );
00471     fprintf( file, "/P { C arc fill } bind def\n" );
00472 
00473     fprintf( file, "\n%% Segment - x2 y2 x1 y1 r g b S\n" );
00474     fprintf( file, "/S { C moveto lineto stroke } bind def\n" );
00475 
00476     fprintf( file, "\n%% Flat-shaded triangle - x3 y3 x2 y2 x1 y1 r g b T\n" );
00477     fprintf( file, "/T { C newpath moveto lineto lineto closepath fill } bind def\n" );
00478 
00479     //  Output Frederic Delhoume's "gouraudtriangle" PostScript fragment.
00480     fputs( "% The gouraudtriangle PostScript fragement below is free\n", file );
00481     fputs( "% written by Frederic Delhoume (delhoume@ilog.fr)\n", file );
00482 
00483     for( int i=0; gouraudtriangleEPS[i]; i++ ){
00484         fprintf( file, "%s\n", gouraudtriangleEPS[i] );
00485     }
00486 
00487     fprintf( file, "\n%g setlinewidth\n", EPS_LINE_WIDTH );
00488 
00489     //  Clear the background like OpenGL had it
00490     fprintf( file, "%g %g %g setrgbcolor\n",   clearColor[0], clearColor[1], clearColor[2] );
00491     fprintf( file, "%g %g %g %g rectfill\n\n", viewport[0],   viewport[1], viewport[2], viewport[3] );
00492 
00493     // Main process
00494     if( doSort ){
00495         spewSortedFeedback( file, size, buffer );
00496     }else{
00497         spewUnsortedFeedback( file, size, buffer );
00498     }
00499 
00500     // Emit EPS trailer
00501     fputs( "\ngrestore\n", file );
00502     fputs( "showpage\n", file );
00503 
00504 }
00505 
00506 
00507 };  //  namespace Graphics
00508 };  //  namespace Teddy
00509