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

WavefrontDisplayDevice.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: WavefrontDisplayDevice.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.28 $       $Date: 2013/04/16 15:50:47 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *
00019  *   Render to a Wavefront Object or "OBJ" file.
00020  *   This file format is one of the most universally supported 3-D model
00021  *   file formats, particular for animation software such as Maya, 
00022  *   3-D Studio, etc.  The file format is simple, but good enough for getting
00023  *   a lot of things done.  The Wavefront format only supports per-facet 
00024  *   colors and materials.  This code currently generates a fixed size set of 
00025  *   color/material entries and indexes into this set based on the active VMD
00026  *   color index.
00027  *
00028  ***************************************************************************/
00029 
00030 #include <stdio.h>
00031 #include <string.h>
00032 #include <math.h>
00033 #include <time.h>
00034 #include "WavefrontDisplayDevice.h"
00035 #include "Matrix4.h"
00036 #include "DispCmds.h"
00037 #include "Inform.h"
00038 #include "utilities.h"
00039 #include "config.h"    // for VMDVERSION string
00040 
00041 #define DASH_LENGTH 0.02f
00042 
00043 #define VMDGENMTLFILE 1
00044 
00045 static int replacefileextension(char * s, 
00046                                 const char * oldextension, 
00047                                 const char * newextension) {
00048   int sz, extsz;
00049   sz = strlen(s);
00050   extsz = strlen(oldextension);
00051 
00052   if (strlen(newextension) != strlen(oldextension))
00053    return -1;
00054 
00055   if (extsz > sz)
00056     return -1;
00057 
00058   if (strupncmp(s + (sz - extsz), oldextension, extsz)) {
00059     return -1;
00060   }
00061 
00062   strcpy(s + (sz - extsz), newextension);
00063 
00064   return 0;
00065 }
00066 
00067 static char * stripleadingfilepath(const char *fullpath) {
00068   int i, j;
00069   char *s = NULL;
00070   int len=strlen(fullpath);
00071   s = (char *) calloc(1, len+1);
00072 
00073   // find last '/' or '\' path separator character 
00074   // and copy remaining string
00075   for (i=0,j=0; i<len; i++) {
00076     if (fullpath[i] == '/' || fullpath[i] == '\\')
00077       j=i;
00078   }
00079 
00080   // char after the last path separator begins shortened path
00081   if (j != 0)
00082     strcpy(s, fullpath+j+1); // strip leading path separators
00083   else
00084     strcpy(s, fullpath);     // nothing to strip off...
00085 
00086   return s;
00087 }
00088 
00089 
00090 // constructor ... call the parent with the right values
00091 WavefrontDisplayDevice::WavefrontDisplayDevice(void) 
00092 : FileRenderer("Wavefront", "Wavefront (OBJ and MTL)", "vmdscene.obj", "true") { }
00093 
00094 // destructor
00095 WavefrontDisplayDevice::~WavefrontDisplayDevice(void) { }
00096 
00097 // emit a representation geometry group line
00098 void WavefrontDisplayDevice::beginrepgeomgroup(const char *s) {
00099   fprintf(outfile, "g %s\n", s);
00100 }
00101 
00102 // emit a comment line
00103 void WavefrontDisplayDevice::comment(const char *s) {
00104   fprintf(outfile, "# %s\n", s);
00105 }
00106 
00107 // draw a point
00108 void WavefrontDisplayDevice::point(float * spdata) {
00109   float vec[3];
00110   // transform the world coordinates
00111   (transMat.top()).multpoint3d(spdata, vec);
00112 
00113   // draw the sphere
00114   fprintf(outfile, "v %5f %5f %5f\n", vec[0], vec[1], -vec[2]);
00115   fprintf(outfile, "p -1\n");
00116 }
00117 
00118 // draw a line from a to b
00119 void WavefrontDisplayDevice::line(float *a, float*b) {
00120   int i, j, test;
00121   float dirvec[3], unitdirvec[3];
00122   float from[3], to[3], tmp1[3], tmp2[3];
00123   float len;
00124    
00125   if (lineStyle == ::SOLIDLINE) {
00126     // transform the world coordinates
00127     (transMat.top()).multpoint3d(a, from);
00128     (transMat.top()).multpoint3d(b, to);
00129 
00130     // draw the solid line
00131     fprintf(outfile, "v %5f %5f %5f\n", from[0], from[1], -from[2]);
00132     fprintf(outfile, "v %5f %5f %5f\n", to[0], to[1], -to[2]);
00133     fprintf(outfile, "l -1 -2\n");
00134   } else if (lineStyle == ::DASHEDLINE) {
00135      // transform the world coordinates
00136     (transMat.top()).multpoint3d(a, tmp1);
00137     (transMat.top()).multpoint3d(b, tmp2);
00138 
00139     // how to create a dashed line
00140     for(i=0; i<3; i++) {
00141       dirvec[i] = tmp2[i] - tmp1[i];  // vector from a to b
00142     }
00143     len = sqrtf(dirvec[0]*dirvec[0] + dirvec[1]*dirvec[1] + dirvec[2]*dirvec[2])
00144 ;
00145     for(i=0;i<3;i++) {
00146       unitdirvec[i] = dirvec[i] / sqrtf(len); // unit vector from a to b
00147     }
00148           
00149     test = 1;
00150     i = 0;
00151     while(test == 1) {
00152       for(j=0;j<3;j++) {
00153         from[j] = (float) (tmp1[j] + (2*i    )*DASH_LENGTH*unitdirvec[j]);
00154           to[j] = (float) (tmp1[j] + (2*i + 1)*DASH_LENGTH*unitdirvec[j]);
00155       }
00156 
00157       if (fabsf(tmp1[0] - to[0]) >= fabsf(dirvec[0])) {
00158         for(j=0;j<3;j++)
00159           to[j] = tmp2[j];
00160         test = 0;
00161       }
00162 
00163       // draw the solid line dash
00164       fprintf(outfile, "v %5f %5f %5f\n", from[0], from[1], -from[2]);
00165       fprintf(outfile, "v %5f %5f %5f\n", to[0], to[1], -to[2]);
00166       fprintf(outfile, "l -1 -2\n");
00167       i++;
00168     }
00169   } else {
00170     msgErr << "WavefrontDisplayDevice: Unknown line style "
00171            << lineStyle << sendmsg;
00172   }
00173 }
00174 
00175 
00176 
00177 
00178 void WavefrontDisplayDevice::triangle(const float *v1, const float *v2, const float *v3, 
00179                                       const float *n1, const float *n2, const float *n3) {
00180   float a[3], b[3], c[3];
00181   float norm1[3], norm2[3], norm3[3];
00182 
00183   // transform the world coordinates
00184   (transMat.top()).multpoint3d(v1, a);
00185   (transMat.top()).multpoint3d(v2, b);
00186   (transMat.top()).multpoint3d(v3, c);
00187 
00188   // and the normals
00189   (transMat.top()).multnorm3d(n1, norm1);
00190   (transMat.top()).multnorm3d(n2, norm2);
00191   (transMat.top()).multnorm3d(n3, norm3);
00192 
00193 #ifdef VMDGENMTLFILE
00194   // set colors
00195   write_cindexmaterial(colorIndex, materialIndex);
00196 #endif
00197                                                        
00198   // draw the triangle 
00199   fprintf(outfile,"v %f %f %f\n", a[0], a[1], a[2]);
00200   fprintf(outfile,"v %f %f %f\n", b[0], b[1], b[2]);
00201   fprintf(outfile,"v %f %f %f\n", c[0], c[1], c[2]);
00202   fprintf(outfile,"vn %.4f %.4f %.4f\n", norm1[0], norm1[1], norm1[2]);
00203   fprintf(outfile,"vn %.4f %.4f %.4f\n", norm2[0], norm2[1], norm2[2]);
00204   fprintf(outfile,"vn %.4f %.4f %.4f\n", norm3[0], norm3[1], norm3[2]);
00205   fprintf(outfile,"f -3//-3 -2//-2 -1//-1\n");
00206 }
00207 
00208 
00209 // use an efficient mesh primitve rather than individual triangles
00210 // when possible.
00211 void WavefrontDisplayDevice::trimesh_c4n3v3(int numverts, float * cnv,
00212                                             int numfacets, int * facets) {
00213   int i;
00214   float vec1[3];
00215   float norm1[3];
00216   const float onethird = (1.0f / 3.0f);
00217 
00218   // write out list of vertices and normals
00219   for (i=0; i<numverts; i++) {
00220     int idx = i*10;
00221 
00222     (transMat.top()).multpoint3d(cnv + idx + 7, vec1);
00223     fprintf(outfile, "v %f %f %f\n", vec1[0], vec1[1], vec1[2]);
00224 
00225     (transMat.top()).multnorm3d(cnv + idx + 4, norm1);
00226     fprintf(outfile, "vn %.4f %.4f %.4f\n", norm1[0], norm1[1], norm1[2]);
00227   }
00228 
00229   // loop over all of the facets in the mesh
00230   for (i=0; i<numfacets*3; i+=3) {
00231     int v0 = facets[i    ];
00232     int v1 = facets[i + 1];
00233     int v2 = facets[i + 2];
00234 
00235 #ifdef VMDGENMTLFILE
00236     // The Wavefront format does not allow per-vertex colors/materials,
00237     // so we use per-facet coloring, averaging the three vertex colors and
00238     // selecting the closest color from the VMD color table.
00239     const float *c1 = cnv + v0 * 10;
00240     const float *c2 = cnv + v1 * 10;
00241     const float *c3 = cnv + v2 * 10;
00242     float r, g, b;
00243     r = (c1[0] + c2[0] + c3[0]) * onethird; // average three vertex colors
00244     g = (c1[1] + c2[1] + c3[1]) * onethird;
00245     b = (c1[2] + c2[2] + c3[2]) * onethird;
00246 
00247     int cindex = nearest_index(r, g, b);
00248     write_cindexmaterial(cindex, materialIndex);
00249 #endif
00250 
00251     // use negative relative indices required for wavefront obj format
00252     v0 -= numverts;
00253     v1 -= numverts;
00254     v2 -= numverts;
00255     fprintf(outfile, "f %d//%d %d//%d %d//%d\n", v0, v0, v1, v1, v2, v2);
00256   }
00257 }
00258 
00259 
00260 // use an efficient mesh primitve rather than individual triangles
00261 // when possible.
00262 void WavefrontDisplayDevice::trimesh_c4u_n3b_v3f(unsigned char *c, char *n,
00263                                                  float *v, int numfacets) {
00264   int i;
00265   float vec1[3];
00266   float norm1[3];
00267   int numverts = 3*numfacets;
00268 
00269   const float onethird = (1.0f / 3.0f); // used for color averaging
00270   const float ci2f = 1.0f / 255.0f; // used for uchar2float and normal conv
00271   const float cn2f = 1.0f / 127.5f;
00272 
00273   // write out list of vertices and normals
00274   for (i=0; i<numverts; i++) {
00275     float ntmp[3];
00276     int idx = i * 3;
00277 
00278     (transMat.top()).multpoint3d(v + idx, vec1);
00279     fprintf(outfile, "v %f %f %f\n", vec1[0], vec1[1], vec1[2]);
00280 
00281     // conversion from GLbyte format, Table 2.6, p. 44 of OpenGL spec 1.2.1
00282     // float = (2c+1)/(2^8-1)
00283     ntmp[0] = n[idx  ] * cn2f + ci2f;
00284     ntmp[1] = n[idx+1] * cn2f + ci2f;
00285     ntmp[2] = n[idx+2] * cn2f + ci2f;
00286     (transMat.top()).multnorm3d(ntmp, norm1);
00287     fprintf(outfile, "vn %.3f %.3f %.3f\n", norm1[0], norm1[1], norm1[2]);
00288   }
00289 
00290   // loop over all of the facets in the mesh
00291   for (i=0; i<numfacets*3; i+=3) {
00292     int idx;
00293 
00294     int v0 = i;
00295     int v1 = i+1;
00296     int v2 = i+2;
00297 
00298 #ifdef VMDGENMTLFILE
00299     // The Wavefront format does not allow per-vertex colors/materials,
00300     // so we use per-facet coloring, averaging the three vertex colors and
00301     // selecting the closest color from the VMD color table.
00302 
00303     // conversion from GLubyte format, Table 2.6, p. 44 of OpenGL spec 1.2.1
00304     // float = c/(2^8-1)
00305     float c0[3], c1[3], c2[3];
00306     idx = v0 * 4;
00307     c0[0] = c[idx    ] * ci2f;
00308     c0[1] = c[idx + 1] * ci2f;
00309     c0[2] = c[idx + 2] * ci2f;
00310 
00311     idx = v1 * 4;
00312     c1[0] = c[idx    ] * ci2f;
00313     c1[1] = c[idx + 1] * ci2f;
00314     c1[2] = c[idx + 2] * ci2f;
00315 
00316     idx = v2 * 4;
00317     c2[0] = c[idx    ] * ci2f;
00318     c2[1] = c[idx + 1] * ci2f;
00319     c2[2] = c[idx + 2] * ci2f;
00320 
00321     float r, g, b;
00322     r = (c0[0] + c1[0] + c2[0]) * onethird; // average three vertex colors
00323     g = (c0[1] + c1[1] + c2[1]) * onethird;
00324     b = (c0[2] + c1[2] + c2[2]) * onethird;
00325 
00326     int cindex = nearest_index(r, g, b);
00327     write_cindexmaterial(cindex, materialIndex);
00328 #endif
00329 
00330     // use negative relative indices required for wavefront obj format
00331     v0 -= numverts;
00332     v1 -= numverts;
00333     v2 -= numverts;
00334     fprintf(outfile, "f %d//%d %d//%d %d//%d\n", v0, v0, v1, v1, v2, v2);
00335   }
00336 }
00337 
00338 
00339 void WavefrontDisplayDevice::tristrip(int numverts, const float * cnv,
00340                                       int numstrips, const int *vertsperstrip,
00341                                       const int *facets) {
00342   int i, strip, t, v = 0;
00343   int stripaddr[2][3] = { {0, 1, 2}, {1, 0, 2} };
00344   float vec1[3];
00345   float norm1[3];
00346   const float onethird = (1.0f / 3.0f);
00347 
00348   // write out list of vertices and normals
00349   for (i=0; i<numverts; i++) {
00350     int idx = i*10;
00351 
00352     (transMat.top()).multpoint3d(cnv + idx + 7, vec1);
00353     fprintf(outfile, "v %f %f %f\n", vec1[0], vec1[1], vec1[2]);
00354 
00355     (transMat.top()).multnorm3d(cnv + idx + 4, norm1);
00356     fprintf(outfile, "vn %f %f %f\n", norm1[0], norm1[1], norm1[2]);
00357   }
00358 
00359   // render triangle strips one triangle at a time
00360   // triangle winding order is:
00361   //   v0, v1, v2, then v2, v1, v3, then v2, v3, v4, etc.
00362   // loop over all of the triangle strips
00363   for (strip=0; strip < numstrips; strip++) {
00364     // loop over all triangles in this triangle strip
00365     for (t = 0; t < (vertsperstrip[strip] - 2); t++) {
00366       // render one triangle, using lookup table to fix winding order
00367       int v0 = facets[v + (stripaddr[t & 0x01][0])];
00368       int v1 = facets[v + (stripaddr[t & 0x01][1])];
00369       int v2 = facets[v + (stripaddr[t & 0x01][2])];
00370 
00371 #ifdef VMDGENMTLFILE
00372       // The Wavefront format does not allow per-vertex colors/materials,
00373       // so we use per-facet coloring, averaging the three vertex colors and
00374       // selecting the closest color from the VMD color table.
00375       const float *c1 = cnv + v0 * 10;
00376       const float *c2 = cnv + v1 * 10;
00377       const float *c3 = cnv + v2 * 10;
00378       float r, g, b;
00379       r = (c1[0] + c2[0] + c3[0]) * onethird; // average three vertex colors
00380       g = (c1[1] + c2[1] + c3[1]) * onethird;
00381       b = (c1[2] + c2[2] + c3[2]) * onethird;
00382 
00383       int cindex = nearest_index(r, g, b);
00384       write_cindexmaterial(cindex, materialIndex);
00385 #endif
00386 
00387       // use negative relative indices required for wavefront obj format
00388       v0 -= numverts;
00389       v1 -= numverts;
00390       v2 -= numverts;
00391       fprintf(outfile, "f %d//%d %d//%d %d//%d\n", v0, v0, v1, v1, v2, v2); 
00392       v++; // move on to next vertex
00393     }
00394     v+=2; // last two vertices are already used by last triangle
00395   }
00396 }
00397 
00398 
00399 int WavefrontDisplayDevice::open_file(const char *filename) {
00400   if (isOpened) {
00401     close_file();
00402   }
00403   if ((outfile = fopen(filename, "w")) == NULL) {
00404     msgErr << "Could not open file " << filename
00405            << " in current directory for writing!" << sendmsg;
00406     return FALSE;
00407   }
00408   my_filename = stringdup(filename);
00409 
00410 #ifdef VMDGENMTLFILE
00411   mtlfilename = stringdup(filename);
00412   if (replacefileextension(mtlfilename, ".obj", ".mtl")) {
00413     msgErr << "Could not generate material filename" << sendmsg;
00414     return FALSE;
00415   }
00416   if ((mtlfile = fopen(mtlfilename, "w")) == NULL) {
00417     msgErr << "Could not open file " << mtlfilename
00418            << " in current directory for writing!" << sendmsg;
00419     return FALSE;
00420   }
00421 #endif
00422 
00423   isOpened = TRUE;
00424   reset_state();
00425   oldColorIndex = -1; 
00426   oldMaterialIndex = -1; 
00427   oldMaterialState = -1; 
00428   return TRUE;
00429 }
00430 
00431 void WavefrontDisplayDevice::close_file(void) {
00432   if (outfile) {
00433     fclose(outfile);
00434     outfile = NULL;
00435   }
00436   delete [] my_filename;
00437   my_filename = NULL;
00438 
00439 #ifdef VMDGENMTLFILE
00440   if (mtlfile) {
00441     fclose(mtlfile);
00442     mtlfile = NULL;
00443   }
00444   delete [] mtlfilename;
00445   mtlfilename = NULL;
00446 #endif
00447 
00448   isOpened = FALSE;
00449 }
00450 
00451 void WavefrontDisplayDevice::write_header(void) {
00452   fprintf(outfile, "# Wavefront OBJ file export by VMD\n");
00453   fprintf(outfile, "# \n");
00454   fprintf(outfile, "# Molecular graphics exported from VMD %s\n", VMDVERSION);
00455   fprintf(outfile, "# http://www.ks.uiuc.edu/Research/vmd/\n");
00456   fprintf(outfile, "# \n");
00457 
00458 #ifdef VMDGENMTLFILE
00459   fprintf(mtlfile, "# Wavefront OBJ MTL file export by VMD\n");
00460   fprintf(mtlfile, "# \n");
00461   fprintf(mtlfile, "# Molecular graphics exported from VMD %s\n", VMDVERSION);
00462   fprintf(mtlfile, "# http://www.ks.uiuc.edu/Research/vmd/\n");
00463   fprintf(mtlfile, "# \n");
00464 
00465   if (mtlfilename) {
00466     char *shortmtlfilename = NULL;
00467     shortmtlfilename = stripleadingfilepath(mtlfilename);
00468     fprintf(outfile, "# Load Material Library paired with this scene:\n");
00469     fprintf(outfile, "mtllib %s\n", shortmtlfilename);
00470     free(shortmtlfilename);
00471   }
00472 
00473   write_material_block();
00474 #endif
00475 }
00476 
00477 void WavefrontDisplayDevice::write_material_block(void) {
00478 #ifdef VMDGENMTLFILE
00479    int n;
00480 
00481    // materials for normal lighting modes
00482    for (n=BEGREGCLRS; n < (BEGREGCLRS + REGCLRS + MAPCLRS); n++) {
00483      float rgb[3];
00484      fprintf(mtlfile, "newmtl vmd_mat_cindex_%d\n", n); 
00485      vec_scale(rgb, 0.0f, matData[n]);
00486      fprintf(mtlfile, "Ka %.2f %.2f %.2f\n", rgb[0], rgb[1], rgb[2]); 
00487      vec_scale(rgb, 0.65f, matData[n]);
00488      fprintf(mtlfile, "Kd %.2f %.2f %.2f\n", rgb[0], rgb[1], rgb[2]); 
00489      vec_scale(rgb, 0.50f, matData[n]);
00490      fprintf(mtlfile, "Ks %.2f %.2f %.2f\n", rgb[0], rgb[1], rgb[2]); 
00491      vec_scale(rgb, 0.0f, matData[n]);
00492      fprintf(mtlfile, "Tf %.2f %.2f %.2f\n", rgb[0], rgb[1], rgb[2]); 
00493      fprintf(mtlfile, "d 1.0\n");
00494      fprintf(mtlfile, "Ns 40.0\n");
00495      fprintf(mtlfile, "illum_4\n");
00496      fprintf(mtlfile, "\n");
00497    }
00498 
00499    // materials for non-lighted modes
00500    for (n=BEGREGCLRS; n < (BEGREGCLRS + REGCLRS + MAPCLRS); n++) {
00501      float rgb[3];
00502      fprintf(mtlfile, "newmtl vmd_nomat_cindex_%d\n", n); 
00503      vec_scale(rgb, 0.0f, matData[n]);
00504      fprintf(mtlfile, "Ka %.2f %.2f %.2f\n", rgb[0], rgb[1], rgb[2]); 
00505      vec_scale(rgb, 0.65f, matData[n]);
00506      fprintf(mtlfile, "Kd %.2f %.2f %.2f\n", rgb[0], rgb[1], rgb[2]); 
00507      fprintf(mtlfile, "illum_0\n");
00508      fprintf(mtlfile, "\n");
00509    }
00510 #endif
00511 }
00512 
00513 void WavefrontDisplayDevice::write_cindexmaterial(int cindex, int material) {
00514 #ifdef VMDGENMTLFILE
00515   if ((oldColorIndex != cindex) ||
00516       (oldMaterialIndex != material) ||
00517       (oldMaterialState != materials_on)) {
00518     if (materials_on) {
00519       fprintf(outfile, "usemtl vmd_mat_cindex_%d\n", cindex);
00520     } else {
00521       fprintf(outfile, "usemtl vmd_nomat_cindex_%d\n", cindex);
00522     }
00523   }
00524 #endif
00525   oldMaterialIndex = material;
00526   oldColorIndex = cindex;
00527   oldMaterialState = materials_on;
00528 }
00529 
00530 void WavefrontDisplayDevice::write_colormaterial(float *rgb, int material) {
00531 #ifdef VMDGENMTLFILE
00532   int cindex = nearest_index(rgb[0], rgb[1], rgb[2]);
00533   write_cindexmaterial(cindex, material);
00534 #endif
00535 }
00536 
00537 void WavefrontDisplayDevice::write_trailer (void) {
00538   msgWarn << "Materials are not exported to Wavefront files.\n";
00539 }
00540 

Generated on Fri May 24 01:49:22 2013 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002