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

R3dDisplayDevice.C

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

Generated on Fri Dec 6 02:45:29 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002