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

R3dDisplayDevice.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr                                                                       
00003  *cr            (C) Copyright 1995-2011 The Board of Trustees of the           
00004  *cr                        University of Illinois                       
00005  *cr                         All Rights Reserved                        
00006  *cr                                                                   
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  * RCS INFORMATION:
00011  *
00012  *      $RCSfile: R3dDisplayDevice.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.86 $       $Date: 2011/02/25 19:20:43 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  * 
00019  * The R3dDisplayDevice implements routines needed to render to a file 
00020  * in raster3d format
00021  *
00022  ***************************************************************************/
00023 
00024 #include <stdio.h>
00025 #include <string.h>
00026 #include <math.h>
00027 #define sqr(x) ((x) * (x))
00028 
00029 #include "R3dDisplayDevice.h"
00030 #include "Matrix4.h"
00031 #include "DispCmds.h"
00032 #include "Inform.h"
00033 #include "utilities.h"
00034 #include "config.h"    // for VMDVERSION string
00035 #include "Hershey.h"   // needed for Hershey font rendering fctns
00036 
00037 #define DEFAULT_RADIUS 0.002f // radius for faking lines with cylinders
00038 #define DASH_LENGTH 0.02f     // dash lengths
00039 
00040 #define currentColor matData[colorIndex]
00041 
00043 
00044 // constructor ... initialize some variables
00045 R3dDisplayDevice::R3dDisplayDevice(void) : 
00046   FileRenderer("Raster3D", "Raster3D 2.7d", "vmdscene.r3d", 
00047                "render -avs < %s | display avs:-") {
00048   reset_vars(); // initialize internal state
00049 }
00050                
00051 //destructor
00052 R3dDisplayDevice::~R3dDisplayDevice(void) { }
00053 
00054 void R3dDisplayDevice::reset_vars(void) {
00055   // Object decl's won't be legal until the header is out.
00056   objLegal = 0;
00057   mat_on = 0;
00058   old_mat_shininess = -1;
00059   old_mat_specular = -1;
00060   old_mat_opacity = -1;
00061 }
00062 
00063 
00065 
00066 void R3dDisplayDevice::text(float *pos, float size, float thickness,
00067                                    const char *str) {
00068   float textpos[3];
00069   float textsize, textthickness;
00070   hersheyhandle hh;
00071 
00072   // transform the world coordinates
00073   (transMat.top()).multpoint3d(pos, textpos);
00074   textsize = size * 1.5f;
00075   textthickness = thickness*DEFAULT_RADIUS;
00076 
00077   while (*str != '\0') {
00078     float lm, rm, x, y, ox, oy;
00079     int draw, odraw;
00080     ox=oy=x=y=0.0f;
00081     draw=odraw=0;
00082 
00083     hersheyDrawInitLetter(&hh, *str, &lm, &rm);
00084     textpos[0] -= lm * textsize;
00085 
00086     while (!hersheyDrawNextLine(&hh, &draw, &x, &y)) {
00087       float oldpt[3], newpt[3];
00088       if (draw) {
00089         newpt[0] = textpos[0] + textsize * x;
00090         newpt[1] = textpos[1] + textsize * y;
00091         newpt[2] = textpos[2];
00092 
00093         if (odraw) {
00094           // if we have both previous and next points, connect them...
00095           oldpt[0] = textpos[0] + textsize * ox;
00096           oldpt[1] = textpos[1] + textsize * oy;
00097           oldpt[2] = textpos[2];
00098 
00099           write_materials();
00100           fprintf(outfile, "5\n"); // flat-ended cylinder
00101           fprintf(outfile, "%7f %7f %7f ", oldpt[0], oldpt[1], oldpt[2]);
00102           fprintf(outfile, "%7f ", textthickness);
00103           fprintf(outfile, "%7f %7f %7f ", newpt[0], newpt[1], newpt[2]);
00104           fprintf(outfile, "%7f ", textthickness);
00105           fprintf(outfile, "%3.2f %3.2f %3.2f\n", sqr(currentColor[0]), 
00106                   sqr(currentColor[1]),  sqr(currentColor[2]));
00107 
00108           write_materials();
00109           fprintf(outfile, "2\n");  // sphere
00110           fprintf(outfile, "%7f %7f %7f ", newpt[0], newpt[1], newpt[2]);
00111           fprintf(outfile, "%7f ", textthickness);
00112           fprintf(outfile, "%3.2f %3.2f %3.2f\n", sqr(currentColor[0]),
00113                   sqr(currentColor[1]), sqr(currentColor[2]));
00114         } else {
00115           // ...otherwise, just draw the next point
00116           write_materials();
00117           fprintf(outfile, "2\n");  // sphere
00118           fprintf(outfile, "%7f %7f %7f ", newpt[0], newpt[1], newpt[2]);
00119           fprintf(outfile, "%7f ", textthickness);
00120           fprintf(outfile, "%3.2f %3.2f %3.2f\n", sqr(currentColor[0]),
00121                   sqr(currentColor[1]), sqr(currentColor[2]));
00122         }
00123       }
00124 
00125       ox=x;
00126       oy=y;
00127       odraw=draw;
00128     }
00129     textpos[0] += rm * textsize;
00130 
00131     str++;
00132   }
00133 }
00134 
00135 
00136 // draw a point
00137 void R3dDisplayDevice::point(float * spdata) {
00138   float vec[3];
00139 
00140   // transform the world coordinates
00141   (transMat.top()).multpoint3d(spdata, vec);
00142 
00143   write_materials();
00144 
00145   // draw the sphere
00146   fprintf(outfile, "2\n");  // sphere
00147   fprintf(outfile, "%7f %7f %7f ", vec[0], vec[1], vec[2]); // center of sphere
00148   fprintf(outfile, "%7f ", float(lineWidth)*DEFAULT_RADIUS ); // the radius of the sphere
00149   fprintf(outfile, "%3.2f %3.2f %3.2f\n", sqr(currentColor[0]), 
00150           sqr(currentColor[1]),  sqr(currentColor[2]));
00151 }
00152 
00153 // draw a sphere
00154 void R3dDisplayDevice::sphere(float * spdata) {
00155   float vec[3];
00156   float radius;
00157     
00158   // transform the world coordinates
00159   (transMat.top()).multpoint3d(spdata, vec);
00160   radius = scale_radius(spdata[3]);
00161   
00162   // write out the current material properties
00163   write_materials();
00164  
00165   // draw the sphere
00166   fprintf(outfile, "2\n");  // sphere
00167   fprintf(outfile, "%7f %7f %7f ", vec[0], vec[1], vec[2]); // center of sphere
00168   fprintf(outfile, "%7f ", radius); // the radius of the sphere
00169   fprintf(outfile, "%3.2f %3.2f %3.2f\n", sqr(currentColor[0]), 
00170           sqr(currentColor[1]), sqr(currentColor[2]));
00171 }
00172 
00173 
00174 // draw a line (cylinder) from a to b
00175 void R3dDisplayDevice::line(float *a, float*b) {
00176     int i, j, test;
00177     float dirvec[3], unitdirvec[3];
00178     float from[3], to[3], tmp1[3], tmp2[3];
00179 
00180     if (lineStyle == ::SOLIDLINE) {
00181         // transform the world coordinates
00182         (transMat.top()).multpoint3d(a, from);
00183         (transMat.top()).multpoint3d(b, to);
00184 
00185         // draw the cylinder
00186         fprintf(outfile, "5\n"); // flat-ended cylinder
00187         fprintf(outfile, "%7f %7f %7f ", from[0], from[1], from[2]); // first point
00188         fprintf(outfile, "%7f ", float(lineWidth)*DEFAULT_RADIUS); // radius 1
00189         fprintf(outfile, "%7f %7f %7f ", to[0], to[1], to[2]); // second point
00190         fprintf(outfile, "%7f ", float(lineWidth)*DEFAULT_RADIUS); // radius 2
00191         fprintf(outfile, "%3.2f %3.2f %3.2f\n", sqr(currentColor[0]), 
00192                 sqr(currentColor[1]), sqr(currentColor[2]));
00193 
00194     } else if (lineStyle == ::DASHEDLINE) {
00195         // transform the world coordinates
00196         (transMat.top()).multpoint3d(a, tmp1);
00197         (transMat.top()).multpoint3d(b, tmp2);
00198 
00199         // how to create a dashed line
00200         vec_sub(dirvec, tmp2, tmp1);  // vector from a to b
00201         vec_copy(unitdirvec, dirvec);
00202         vec_normalize(unitdirvec);    // unit vector from a to b
00203         test = 1;
00204         i = 0;
00205         while (test == 1) {
00206             for (j=0; j<3; j++) {
00207               from[j] = (float) (tmp1[j] + (2*i    )*DASH_LENGTH*unitdirvec[j]);
00208                 to[j] = (float) (tmp1[j] + (2*i + 1)*DASH_LENGTH*unitdirvec[j]);
00209             }
00210             if (fabsf(tmp1[0] - to[0]) >= fabsf(dirvec[0])) {
00211               vec_copy(to, tmp2);
00212               test = 0;
00213             }
00214 
00215             // draw the cylinder
00216             fprintf(outfile, "5\n"); // flat-ended cylinder
00217             fprintf(outfile, "%7f %7f %7f ", from[0], from[1], from[2]); // first point
00218             fprintf(outfile, "%7f ", float(lineWidth)*DEFAULT_RADIUS); // radius 1
00219             fprintf(outfile, "%7f %7f %7f ", to[0], to[1], to[2]); // second point
00220             fprintf(outfile, "%7f ", float(lineWidth)*DEFAULT_RADIUS); // radius 2
00221             fprintf(outfile, "%3.2f %3.2f %3.2f\n", sqr(currentColor[0]), 
00222                     sqr(currentColor[1]), sqr(currentColor[2]));
00223 
00224             i++;
00225         }
00226 
00227     } else {
00228         msgErr << "R3dDisplayDevice: Unknown line style " << lineStyle << sendmsg;
00229     }
00230 
00231 }
00232 
00233 // draw a cylinder
00234 void R3dDisplayDevice::cylinder(float *a, float *b, float r, int) {
00235 
00236   float vec1[3], vec2[3];
00237   float radius;
00238   
00239   // transform the world coordinates
00240   (transMat.top()).multpoint3d(a, vec1);
00241   (transMat.top()).multpoint3d(b, vec2);
00242   radius = scale_radius(r);
00243 
00244   write_materials();
00245 
00247   // ignore the 'filled' flag
00248   fprintf(outfile, "5\n"); // flat-ended cylinder
00249   fprintf(outfile, "%7f %7f %7f ", vec1[0], vec1[1], vec1[2]); // first point
00250   fprintf(outfile, "%7f ", radius); // radius 1
00251   fprintf(outfile, "%7f %7f %7f ", vec2[0], vec2[1], vec2[2]); // second point
00252   fprintf(outfile, "%7f ", radius); // radius 2
00253   fprintf(outfile, "%3.2f %3.2f %3.2f\n", sqr(currentColor[0]), 
00254           sqr(currentColor[1]),  sqr(currentColor[2]));
00255 
00256 }
00257 
00258 // draw a triangle
00259 void R3dDisplayDevice::triangle(const float *a, const float *b, const float *c, const float *n1, const float *n2, const float *n3) {
00260 
00261   float vec1[3], vec2[3], vec3[3];
00262   float norm1[3], norm2[3], norm3[3];
00263   
00264   // transform the world coordinates
00265   (transMat.top()).multpoint3d(a, vec1);
00266   (transMat.top()).multpoint3d(b, vec2);
00267   (transMat.top()).multpoint3d(c, vec3);
00268 
00269   // transform the normals
00270   (transMat.top()).multnorm3d(n1, norm1);
00271   (transMat.top()).multnorm3d(n2, norm2);
00272   (transMat.top()).multnorm3d(n3, norm3);
00273 
00274   write_materials();
00275 
00276   // draw the triangle
00277   fprintf(outfile, "1\n"); // triangle
00278   fprintf(outfile, "%7f %7f %7f ", vec1[0], vec1[1], vec1[2]); 
00279   fprintf(outfile, "%7f %7f %7f ", vec2[0], vec2[1], vec2[2]); 
00280   fprintf(outfile, "%7f %7f %7f ", vec3[0], vec3[1], vec3[2]);
00281   fprintf(outfile, "%3.2f %3.2f %3.2f\n", sqr(currentColor[0]), 
00282           sqr(currentColor[1]),  sqr(currentColor[2]));
00283 
00284   fprintf(outfile, "7\n"); // triangle normals
00285   fprintf(outfile, "%7f %7f %7f ",  norm1[0], norm1[1], norm1[2]);
00286   fprintf(outfile, "%7f %7f %7f ",  norm2[0], norm2[1], norm2[2]);
00287   fprintf(outfile, "%7f %7f %7f\n", norm3[0], norm3[1], norm3[2]);
00288 }
00289 
00290 // draw a three-color triangle
00291 void R3dDisplayDevice::tricolor(const float *a, const float *b, const float *c, const float *n1, const float *n2, const float *n3, const float *c1, const float *c2, const float *c3) {
00292 
00293   float vec1[3], vec2[3], vec3[3];
00294   float norm1[3], norm2[3], norm3[3];
00295 
00296   // transform the world coordinates
00297   (transMat.top()).multpoint3d(a, vec1);
00298   (transMat.top()).multpoint3d(b, vec2);
00299   (transMat.top()).multpoint3d(c, vec3);
00300 
00301   // transform the normals
00302   (transMat.top()).multnorm3d(n1, norm1);
00303   (transMat.top()).multnorm3d(n2, norm2);
00304   (transMat.top()).multnorm3d(n3, norm3);
00305 
00306   write_materials();
00307 
00308   // draw the triangle
00309   fprintf(outfile, "1\n");
00310   fprintf(outfile, "%7f %7f %7f ", vec1[0], vec1[1], vec1[2]);
00311   fprintf(outfile, "%7f %7f %7f ", vec2[0], vec2[1], vec2[2]);
00312   fprintf(outfile, "%7f %7f %7f ", vec3[0], vec3[1], vec3[2]);
00313   fprintf(outfile, "%3.2f %3.2f %3.2f\n", sqr(currentColor[0]),
00314           sqr(currentColor[1]), sqr(currentColor[2]));
00315 
00316   fprintf(outfile, "7\n"); // triangle normals
00317   fprintf(outfile, "%7f %7f %7f ",  norm1[0], norm1[1], norm1[2]);
00318   fprintf(outfile, "%7f %7f %7f ",  norm2[0], norm2[1], norm2[2]);
00319   fprintf(outfile, "%7f %7f %7f\n", norm3[0], norm3[1], norm3[2]);
00320 
00321   // now the colors at the three vertices
00322   fprintf(outfile, "17\n");
00323   fprintf(outfile, "%7f %7f %7f %7f %7f %7f %7f %7f %7f\n",
00324           c1[0], c1[1], c1[2], c2[0], c2[1], c2[2], c3[0], c3[1], c3[2]);
00325 }
00326 
00327 void R3dDisplayDevice::comment(const char *s) {
00328   int i=0, length;
00329   char buf[71];
00330   const char *index;
00331 
00332   if (!objLegal) return;
00333 
00334   length = strlen(s);
00335   index = s;
00336 
00337   while (i*70 < length) {
00338     strncpy(buf, index, 70);
00339     buf[70] = '\0';
00340     fprintf (outfile, "# %s\n", buf);
00341     index += 70;
00342     i++;
00343   }
00344 }
00345 
00347 
00348 // initialize the file for output
00349 void R3dDisplayDevice::write_header() {
00350     int tileX, tileY;
00351     int nTilesX, nTilesY;
00352     int i, nlights;
00353     float lightshare;
00354     float scale;
00355 
00356     fprintf(outfile, "# \n");
00357     fprintf(outfile, "# Molecular graphics export from VMD %s\n", VMDVERSION);
00358     fprintf(outfile, "# http://www.ks.uiuc.edu/Research/vmd/\n");
00359     fprintf(outfile, "# Requires Raster3D version 2.7d or later\n");
00360     fprintf(outfile, "# \n");
00361 
00362     fprintf(outfile, "r3d input script\n");
00363 
00364     // Raster3D does not allow you to specify an exact image size. Instead,
00365     // you specify a number of square tiles (maximum of 192) and then specify
00366     // a resolution (in pixels) per tile (maximum of 36). We want to choose
00367     // the tile size as small as possible and use as many tiles as possible
00368     // so that we get the best approximation of VMD's actual screen size.
00369     //
00370     // This is slightly complicated by the fact that due to antialiasing, the
00371     // tile size must be divisible by 3.
00372 
00373     tileX = 2;
00374     while (xSize / tileX > 192) {
00375         tileX += 2;
00376         if (tileX > 36) {
00377             tileX -= 2;
00378             msgInfo << "Warning: The Raster3D output image has too high a resolution" << sendmsg;
00379             msgInfo << "to be properly rendered. Writing the file anyway, but Raster3D" << sendmsg;
00380             msgInfo << "will probably give an error." << sendmsg;
00381             break;
00382         }
00383     }
00384 
00385     tileY = 2;
00386     while (ySize / tileY > 192) {
00387         tileY += 2;
00388         if (tileY > 36) {
00389             tileY -= 2;
00390             if (xSize / tileX > 192) {
00391                 msgInfo << "Warning: The Raster3D output image has too high a resolution" << sendmsg;
00392                 msgInfo << "to be properly rendered. Writing the file anyway, but Raster3D" << sendmsg;
00393                 msgInfo << "will probably give an error." << sendmsg;
00394             }
00395             break;
00396         }
00397     }
00398 
00399     // Now that we've chosen a value for the tile size, we choose the number
00400     // of tiles to match, as closely as possible, VMD's screen size.
00401 
00402     nTilesX = xSize / tileX;
00403     nTilesY = ySize / tileY;
00404     if (xSize % tileX >= tileX / 2) nTilesX++;
00405     if (ySize % tileY >= tileY / 2) nTilesY++;
00406 
00407     fprintf(outfile, "%d %d          tiles in x,y\n", nTilesX, nTilesY);
00408     fprintf(outfile, "%d %d          computing pixels per tile\n", tileX, tileY);
00409     fprintf(outfile, "4              alti-aliasing scheme 4; 3x3 -> 2x2\n");
00410     fprintf(outfile, "%3.2f %3.2f %3.2f background color\n", 
00411             backColor[0], backColor[1], backColor[2]);
00412     fprintf(outfile, "T              shadows on\n");
00413     fprintf(outfile, "20             Phong power\n");
00414     fprintf(outfile, "1.00           secondary light contribution\n");
00415     fprintf(outfile, "0.10           ambient light contribution\n");
00416     fprintf(outfile, "0.50           specular reflection component\n");
00417 
00418     // Raster3D only allows us to tell it the ratio between the image's narrower
00419     // dimension and the distance from the eye to the viewing plane (in world
00420     // coordinates). Oh well.
00421 
00422     switch (projection()) {
00423 
00424         case DisplayDevice::ORTHOGRAPHIC:
00425             fprintf(outfile, "0              Eye position (orthographic mode)\n");
00426             break;
00427 
00428         case DisplayDevice::PERSPECTIVE:
00429         default:
00430             if (Aspect > 1) fprintf(outfile, "%6.2f         Eye position\n",
00431                 (-zDist + eyePos[2]) / vSize);
00432             else fprintf(outfile, "%6.2f         Eye position\n",
00433                 (-zDist + eyePos[2]) / vSize / Aspect);
00434             break;
00435 
00436     }
00437 
00438     // All light sources defined as Raster3d 2.3+ glow lights, not
00439     // in the header...
00440     fprintf(outfile, "1 0 0          main light source position\n");
00441 
00442     // We need to compute a scaling factor for the Raster3D objects. Depending on
00443     // whether our image is wider than it is tall, we give Raster3D either the
00444     // horizontal scaling factor or the vertical scaling factor (Raster3D doesn't
00445     // allow us to specify both).
00446 
00447     if (Aspect > 1) scale = vSize / 2;
00448     else scale = vSize * Aspect / 2;
00449 
00450     // Global transformation matrix for objects.
00451     fprintf(outfile, "1 0 0 0        global xform matrix\n");
00452     fprintf(outfile, "0 1 0 0\n");
00453     fprintf(outfile, "0 0 1 0\n");
00454     fprintf(outfile, "0 0 0 %.3f\n", scale);
00455 
00456     fprintf(outfile, "3\n");
00457     fprintf(outfile, "*\n*\n*\n");
00458 
00459     // Define additional light sources, if any. Raster3d uses a light-
00460     // sharing model; all light sources affect a percentage of the total
00461     // lighting system. This percentage must be determined in advance.
00462     nlights = 0;
00463     for (i = 0; i < DISP_LIGHTS; i++)
00464         if (lightState[i].on) nlights++;
00465 
00466     // Must use ?: operator to avoid divide by zero
00467     lightshare = nlights ? (1 / (float) nlights) : 0;
00468 
00469     // Now output all of the lights.
00470     for (i = 0; i < DISP_LIGHTS; i++) {
00471         if (lightState[i].on) {
00472             fprintf(outfile, "13\n%f %f %f 100 %f 0 20 1 1 1\n",
00473                     lightState[i].pos[0], lightState[i].pos[1], lightState[i].pos[2],
00474                     lightshare);
00475         }
00476     }
00477  
00478     // and that's it for the header.  next comes free format 
00479     // triangle, sphere, and cylinder descriptors
00480     objLegal = 1;
00481 }
00482 
00483 void R3dDisplayDevice::write_trailer(void) {
00484   // if we need to, turn material properties off
00485   close_materials();
00486 
00487   msgInfo << "Raster3D file generation finished" << sendmsg;
00488 
00489   reset_vars(); // reset internal state
00490 }
00491 
00492 // Writes out the current material properties as a
00493 // material modifier (object type 8)
00494 void R3dDisplayDevice::write_materials(void) {
00495    // Format of material definitions:
00496    //
00497    // 8
00498    // MPHONG MSPEC SR,SG,SB CLRITY OPTS(4)
00499    //    where MPHONG   - phong parameter for specular highlighting
00500    //          MSPEC    - specular scattering contribution
00501    //          SR,SG,SB - color of reflected light
00502    //          CLRITY   - opacity, 0.0=opaque, 1.0=transparent
00503    //          OPTS(4)  - zero, except for OPT(1), which controls
00504    //                     rendering of self-occluding objects
00505    // 9
00506 
00507    if (!mat_on) {
00508      fprintf(outfile, "8\n");
00509      //  Raster3D cannot tolerate inconsistent surface normals, so we
00510      //  have to tell it to flip them for itself.  This format string
00511      //  tell is to do that for us.  This is also necessary for things
00512      //  like surfaces which are inherently "two-sided", particularly
00513      //  if/when clipping planes are used.
00514 #if 1
00515      // auto-flip normals 
00516      fprintf(outfile, "%.3f %.3f 1 1 1 %.3f 2 0 0 0\n",
00517 #else
00518      // don't auto-flip normals 
00519      fprintf(outfile, "%.3f %.3f 1 1 1 %.3f 0 0 0 0\n",
00520 #endif
00521         mat_shininess, mat_specular, 1 - mat_opacity);
00522 
00523      old_mat_shininess = mat_shininess;
00524      old_mat_specular = mat_specular;
00525      old_mat_opacity = mat_opacity;
00526 
00527      mat_on = 1;
00528    }
00529    else if (mat_shininess != old_mat_shininess ||
00530             mat_specular != old_mat_specular ||
00531             mat_opacity != old_mat_opacity) {
00532      fprintf(outfile, "9\n");
00533      fprintf(outfile, "8\n");
00534      fprintf(outfile, "%.3f %.3f 1 1 1 %.3f 0 0 0 0\n",
00535         mat_shininess, mat_specular, 1 - mat_opacity);
00536 
00537      old_mat_shininess = mat_shininess;
00538      old_mat_specular = mat_specular;
00539      old_mat_opacity = mat_opacity;
00540    }
00541 
00542    return;
00543 }
00544 
00545 void R3dDisplayDevice::close_materials(void) {
00546   if (mat_on) {
00547     fprintf(outfile, "9\n");
00548     mat_on = 0;
00549   }
00550   return;
00551 }

Generated on Tue May 22 01:48:10 2012 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002