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