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 /*************************************************************************** 00010 * RCS INFORMATION: 00011 * 00012 * $RCSfile: LibTachyonDisplayDevice.C,v $ 00013 * $Author: johns $ $Locker: $ $State: Exp $ 00014 * $Revision: 1.99 $ $Date: 2021年03月06日 20:51:52 $ 00015 * 00016 ***************************************************************************/ 00022 #include <string.h> 00023 #include <stdio.h> 00024 #include <stdlib.h> 00025 #include <math.h> 00026 #include "LibTachyonDisplayDevice.h" 00027 #include "Matrix4.h" 00028 #include "DispCmds.h" 00029 #include "Inform.h" 00030 #include "utilities.h" 00031 #include "config.h" // needed for default image viewer 00032 #include "Hershey.h" // needed for Hershey font rendering fctns 00033 00034 #if !(((TACHYON_MAJOR_VERSION >= 0) && (TACHYON_MINOR_VERSION >= 99)) || ((TACHYON_MAJOR_VERSION == 0) && (TACHYON_MINOR_VERSION == 99) && (TACHYON_PATCH_VERSION >= 0))) 00035 #error "LibTachyonDisplayDevice requires Tachyon version 0.99.0 or higher." 00036 #endif 00037 00038 #define DEFAULT_RADIUS 0.002f 00039 #define DASH_LENGTH 0.02f 00040 00041 extern "C" { 00042 00043 void vmd_rt_ui_message(int a, char * msg) { 00044 printf("Tachyon) %s\n", msg); 00045 } 00046 00047 void vmd_rt_ui_progress(int percent) { 00048 printf("\rTachyon) Rendering progress: %3d%% complete \r", percent); 00049 fflush(stdout); 00050 } 00051 00052 } 00053 00055 LibTachyonDisplayDevice::LibTachyonDisplayDevice(VMDApp *app) : FileRenderer ("TachyonInternal", "Tachyon (internal, in-memory rendering)", "vmdscene.tga", DEF_VMDIMAGEVIEWER) { 00056 vmdapp = app; // save VMDApp handle for GPU memory management routines 00057 00058 reset_vars(); // initialize internal state 00059 trt_timer = wkf_timer_create(); 00060 00061 #if 0 && defined(VMDMPI) 00062 // init scene-independent parts of Tachyon library 00063 parallel_group = 0; 00064 if (getenv("VMDNOMPI") != NULL || getenv("VMDTACHYONNOMPI") != NULL) { 00065 rt_initialize_nompi(); 00066 } else { 00067 // set Tachyon to use MPI_COMM_WORLD initially. 00068 rt_initialize_mpi_comm_world(); 00069 00070 #if 1 00071 // Unless overridden, every VMD node runs Tachyon independently of one another, 00072 // with each node in its own communicator... 00073 if (getenv("VMDTACHYONWORKGROUP") == NULL) { 00074 parallel_group = -1; 00075 rt_set_mpi_comm_world_split_all(); 00076 } else { 00077 parallel_group = atoi(getenv("VMDTACHYONWORKGROUP")); 00078 rt_set_mpi_comm_world_split(parallel_group, 0); 00079 } 00080 #endif 00081 } 00082 #else 00083 rt_initialize(0, NULL); // init scene-independent parts of Tachyon library 00084 #endif 00085 00086 #if defined(VMDMPI) 00087 // When VMD is running with MPI enabled, we disable console output 00088 // for Tachyon rendering status and progress messages. If VMD 00089 // was compiled with MPI support, we only turn on console output 00090 // when we detect that MPI has been disabled. 00091 if (getenv("VMDNOMPI") != NULL) { 00092 // rt_set_ui_message(vmd_rt_ui_message); 00093 rt_set_ui_progress(vmd_rt_ui_progress); 00094 } 00095 #else 00096 // rt_set_ui_message(vmd_rt_ui_message); 00097 rt_set_ui_progress(vmd_rt_ui_progress); 00098 #endif 00099 00100 // Add supported file formats 00101 formats.add_name("Auto", 0); 00102 formats.add_name("BMP", 0); 00103 formats.add_name("PPM", 0); 00104 formats.add_name("PPM48", 0); 00105 formats.add_name("PSD48", 0); 00106 formats.add_name("RGB", 0); 00107 formats.add_name("TGA", 0); 00108 00109 // Default image format depends on platform 00110 curformat = 0; 00111 00112 // Set default aa level 00113 has_aa = TRUE; 00114 aasamples = 12; 00115 aosamples = 12; 00116 } 00117 00118 LibTachyonDisplayDevice::~LibTachyonDisplayDevice(void) { 00119 rt_finalize(); // shut down Tachyon library 00120 wkf_timer_destroy(trt_timer); 00121 } 00122 00123 00125 00126 // reset internal state between renders 00127 void LibTachyonDisplayDevice::reset_vars(void) { 00128 inclipgroup = 0; // not currently in a clipping group 00129 involtex = 0; // volume texturing disabled 00130 voltexID = -1; // invalid texture ID 00131 memset(xplaneeq, 0, sizeof(xplaneeq)); 00132 memset(yplaneeq, 0, sizeof(xplaneeq)); 00133 memset(zplaneeq, 0, sizeof(xplaneeq)); 00134 } 00135 00136 00138 00139 void LibTachyonDisplayDevice::text(float *pos, float size, float thickness, 00140 const char *str) { 00141 float textpos[3]; 00142 float textsize, textthickness; 00143 hersheyhandle hh; 00144 00145 // transform the world coordinates 00146 (transMat.top()).multpoint3d(pos, textpos); 00147 textsize = size * 1.5f; 00148 textthickness = thickness*DEFAULT_RADIUS; 00149 00150 // create texture to use for text rendering 00151 void *tex=tex_cindexmaterial(colorIndex, materialIndex); 00152 00153 while (*str != '0円') { 00154 float lm, rm, x, y, ox, oy; 00155 int draw, odraw; 00156 ox=oy=x=y=0.0f; 00157 draw=odraw=0; 00158 00159 hersheyDrawInitLetter(&hh, *str, &lm, &rm); 00160 textpos[0] -= lm * textsize; 00161 00162 while (!hersheyDrawNextLine(&hh, &draw, &x, &y)) { 00163 float oldpt[3], newpt[3]; 00164 if (draw) { 00165 newpt[0] = textpos[0] + textsize * x; 00166 newpt[1] = textpos[1] + textsize * y; 00167 newpt[2] = textpos[2]; 00168 00169 if (odraw) { 00170 // if we have both previous and next points, connect them... 00171 oldpt[0] = textpos[0] + textsize * ox; 00172 oldpt[1] = textpos[1] + textsize * oy; 00173 oldpt[2] = textpos[2]; 00174 00175 rt_fcylinder(rtscene, tex, rt_vector(oldpt[0], oldpt[1], -oldpt[2]), 00176 rt_vector(newpt[0]-oldpt[0], newpt[1]-oldpt[1], -newpt[2]+oldpt[2]), 00177 textthickness); 00178 rt_sphere(rtscene, tex, 00179 rt_vector(newpt[0], newpt[1], -newpt[2]), textthickness); 00180 } else { 00181 // ...otherwise, just draw the next point 00182 rt_sphere(rtscene, tex, 00183 rt_vector(newpt[0], newpt[1], -newpt[2]), textthickness); 00184 } 00185 } 00186 00187 ox=x; 00188 oy=y; 00189 odraw=draw; 00190 } 00191 textpos[0] += rm * textsize; 00192 00193 str++; 00194 } 00195 } 00196 00197 00198 // draw a point 00199 void LibTachyonDisplayDevice::point(float * spdata) { 00200 float vec[3]; 00201 void *tex; 00202 // transform the world coordinates 00203 (transMat.top()).multpoint3d(spdata, vec); 00204 00205 // draw the sphere 00206 tex=tex_cindexmaterial(colorIndex, materialIndex); 00207 rt_sphere(rtscene, tex, 00208 rt_vector(vec[0], vec[1], -vec[2]), 00209 float(lineWidth)*DEFAULT_RADIUS); 00210 } 00211 00212 00213 // draw a sphere 00214 void LibTachyonDisplayDevice::sphere(float * spdata) { 00215 float vec[3]; 00216 float radius; 00217 void *tex; 00218 00219 // transform the world coordinates 00220 (transMat.top()).multpoint3d(spdata, vec); 00221 radius = scale_radius(spdata[3]); 00222 00223 // draw the sphere 00224 tex=tex_cindexmaterial(colorIndex, materialIndex); 00225 rt_sphere(rtscene, tex, rt_vector(vec[0], vec[1], -vec[2]), radius); 00226 } 00227 00228 00229 // draw a sphere array 00230 void LibTachyonDisplayDevice::sphere_array(int spnum, int spres, float *centers, float *radii, float *colors) { 00231 float vec[3]; 00232 float radius; 00233 int i, ind; 00234 void *tex; 00235 00236 ind = 0; 00237 for (i=0; i<spnum; i++) { 00238 // transform the world coordinates 00239 (transMat.top()).multpoint3d(¢ers[ind], vec); 00240 radius = scale_radius(radii[i]); 00241 00242 // draw the sphere 00243 tex=tex_colormaterial(&colors[ind], materialIndex); 00244 rt_sphere(rtscene, tex, rt_vector(vec[0], vec[1], -vec[2]), radius); 00245 ind += 3; // next sphere 00246 } 00247 00248 // set final color state after array has been drawn 00249 ind=(spnum-1)*3; 00250 super_set_color(nearest_index(colors[ind], colors[ind+1], colors[ind+2])); 00251 } 00252 00253 00254 // draw a line (cylinder) from a to b 00255 void LibTachyonDisplayDevice::line(float *a, float*b) { 00256 int i, j, test; 00257 float dirvec[3], unitdirvec[3]; 00258 float from[3], to[3], tmp1[3], tmp2[3]; 00259 void *tex; 00260 00261 if (lineStyle == ::SOLIDLINE) { 00262 // transform the world coordinates 00263 (transMat.top()).multpoint3d(a, from); 00264 (transMat.top()).multpoint3d(b, to); 00265 00266 // draw the cylinder 00267 tex=tex_cindexmaterial(colorIndex, materialIndex); 00268 rt_fcylinder(rtscene, tex, 00269 rt_vector(from[0], from[1], -from[2]), 00270 rt_vector(to[0]-from[0], to[1]-from[1], -to[2]+from[2]), 00271 float(lineWidth)*DEFAULT_RADIUS); 00272 00273 } else if (lineStyle == ::DASHEDLINE) { 00274 // transform the world coordinates 00275 (transMat.top()).multpoint3d(a, tmp1); 00276 (transMat.top()).multpoint3d(b, tmp2); 00277 00278 // how to create a dashed line 00279 vec_sub(dirvec, tmp2, tmp1); // vector from a to b 00280 vec_copy(unitdirvec, dirvec); 00281 vec_normalize(unitdirvec); // unit vector from a to b 00282 test = 1; 00283 i = 0; 00284 while (test == 1) { 00285 for (j=0; j<3; j++) { 00286 from[j] = (float) (tmp1[j] + (2*i )*DASH_LENGTH*unitdirvec[j]); 00287 to[j] = (float) (tmp1[j] + (2*i + 1)*DASH_LENGTH*unitdirvec[j]); 00288 } 00289 if (fabsf(tmp1[0] - to[0]) >= fabsf(dirvec[0])) { 00290 vec_copy(to, tmp2); 00291 test = 0; 00292 } 00293 00294 // draw the cylinder 00295 tex=tex_cindexmaterial(colorIndex, materialIndex); 00296 rt_fcylinder(rtscene, tex, 00297 rt_vector(from[0], from[1], -from[2]), 00298 rt_vector(to[0]-from[0], to[1]-from[1], -to[2]+from[2]), 00299 float(lineWidth)*DEFAULT_RADIUS); 00300 i++; 00301 } 00302 } else { 00303 msgErr << "LibTachyonDisplayDevice: Unknown line style " 00304 << lineStyle << sendmsg; 00305 } 00306 } 00307 00308 00309 // draw a cylinder 00310 void LibTachyonDisplayDevice::cylinder(float *a, float *b, float r, int filled) { 00311 float from[3], to[3], norm[3]; 00312 float radius; 00313 void * tex; 00314 00315 // transform the world coordinates 00316 (transMat.top()).multpoint3d(a, from); 00317 (transMat.top()).multpoint3d(b, to); 00318 radius = scale_radius(r); 00319 00320 // draw the cylinder 00321 tex=tex_cindexmaterial(colorIndex, materialIndex); 00322 rt_fcylinder(rtscene, tex, 00323 rt_vector(from[0], from[1], -from[2]), 00324 rt_vector(to[0]-from[0], to[1]-from[1], -to[2]+from[2]), 00325 radius); 00326 00327 // Cylinder caps? 00328 if (filled) { 00329 float div; 00330 00331 norm[0] = to[0] - from[0]; 00332 norm[1] = to[1] - from[1]; 00333 norm[2] = to[2] - from[2]; 00334 00335 div = 1.0f / sqrtf(norm[0]*norm[0] + norm[1]*norm[1] + norm[2]*norm[2]); 00336 norm[0] *= div; 00337 norm[1] *= div; 00338 norm[2] *= div; 00339 00340 if (filled & CYLINDER_TRAILINGCAP) { 00341 rt_ring(rtscene, tex, 00342 rt_vector(from[0], from[1], -from[2]), 00343 rt_vector(norm[0], norm[1], -norm[2]), 00344 0.0, radius); 00345 } 00346 00347 if (filled & CYLINDER_LEADINGCAP) { 00348 rt_ring(rtscene, tex, 00349 rt_vector(to[0], to[1], -to[2]), 00350 rt_vector(-norm[0], -norm[1], norm[2]), 00351 0.0, radius); 00352 } 00353 } 00354 } 00355 00356 // draw a triangle 00357 void LibTachyonDisplayDevice::triangle(const float *a, const float *b, const float *c, const float *n1, const float *n2, const float *n3) { 00358 float vec1[3], vec2[3], vec3[3]; 00359 float norm1[3], norm2[3], norm3[3]; 00360 void *tex; 00361 00362 // transform the world coordinates 00363 (transMat.top()).multpoint3d(a, vec1); 00364 (transMat.top()).multpoint3d(b, vec2); 00365 (transMat.top()).multpoint3d(c, vec3); 00366 00367 // and the normals 00368 (transMat.top()).multnorm3d(n1, norm1); 00369 (transMat.top()).multnorm3d(n2, norm2); 00370 (transMat.top()).multnorm3d(n3, norm3); 00371 00372 // draw the triangle 00373 tex=tex_cindexmaterial(colorIndex, materialIndex); 00374 rt_stri(rtscene, tex, 00375 rt_vector(vec1[0], vec1[1], -vec1[2]), 00376 rt_vector(vec2[0], vec2[1], -vec2[2]), 00377 rt_vector(vec3[0], vec3[1], -vec3[2]), 00378 rt_vector(-norm1[0], -norm1[1], norm1[2]), 00379 rt_vector(-norm2[0], -norm2[1], norm2[2]), 00380 rt_vector(-norm3[0], -norm3[1], norm3[2])); 00381 } 00382 00383 00384 // draw triangle with per-vertex colors 00385 void LibTachyonDisplayDevice::tricolor(const float * xyz1, const float * xyz2, const float * xyz3, 00386 const float * n1, const float * n2, const float * n3, 00387 const float *c1, const float *c2, const float *c3) { 00388 float vec1[3], vec2[3], vec3[3]; 00389 float norm1[3], norm2[3], norm3[3]; 00390 float rgb[3]; 00391 void *tex; 00392 00393 // transform the world coordinates 00394 (transMat.top()).multpoint3d(xyz1, vec1); 00395 (transMat.top()).multpoint3d(xyz2, vec2); 00396 (transMat.top()).multpoint3d(xyz3, vec3); 00397 00398 // and the normals 00399 (transMat.top()).multnorm3d(n1, norm1); 00400 (transMat.top()).multnorm3d(n2, norm2); 00401 (transMat.top()).multnorm3d(n3, norm3); 00402 00403 rgb[0] = 0.0; 00404 rgb[1] = 0.0; 00405 rgb[2] = 0.0; 00406 00407 // draw the triangle 00408 tex=tex_colormaterial(rgb, materialIndex); 00409 00410 if (!involtex) { 00411 rt_vcstri(rtscene, tex, 00412 rt_vector(vec1[0], vec1[1], -vec1[2]), 00413 rt_vector(vec2[0], vec2[1], -vec2[2]), 00414 rt_vector(vec3[0], vec3[1], -vec3[2]), 00415 rt_vector(-norm1[0], -norm1[1], norm1[2]), 00416 rt_vector(-norm2[0], -norm2[1], norm2[2]), 00417 rt_vector(-norm3[0], -norm3[1], norm3[2]), 00418 rt_color(c1[0], c1[1], c1[2]), 00419 rt_color(c2[0], c2[1], c2[2]), 00420 rt_color(c3[0], c3[1], c3[2])); 00421 } else { 00422 rt_stri(rtscene, tex, 00423 rt_vector(vec1[0], vec1[1], -vec1[2]), 00424 rt_vector(vec2[0], vec2[1], -vec2[2]), 00425 rt_vector(vec3[0], vec3[1], -vec3[2]), 00426 rt_vector(-norm1[0], -norm1[1], norm1[2]), 00427 rt_vector(-norm2[0], -norm2[1], norm2[2]), 00428 rt_vector(-norm3[0], -norm3[1], norm3[2])); 00429 } 00430 } 00431 00432 00433 // draw triangle strips with per-vertex colors 00434 void LibTachyonDisplayDevice::tristrip(int numverts, const float *cnv, 00435 int numstrips, const int *vertsperstrip, 00436 const int *facets) { 00437 int i; 00438 float rgb[3]; 00439 void *tex; 00440 00441 Matrix4 topMatrix = transMat.top(); 00442 00443 /* transform all vertices and normals, copy colors as-is */ 00444 float *tcnv = new float[numverts * 10]; 00445 00446 for (i=0; i<numverts; i++) { 00447 int addr = i * 10; 00448 int j; 00449 00450 for (j=0; j<3; j++) 00451 tcnv[addr + j] = cnv[addr + j]; 00452 00453 topMatrix.multnorm3d(&cnv[addr + 4], &tcnv[addr + 4]); 00454 tcnv[addr + 4] = -tcnv[addr + 4]; 00455 tcnv[addr + 5] = -tcnv[addr + 5]; 00456 topMatrix.multpoint3d(&cnv[addr + 7], &tcnv[addr + 7]); 00457 tcnv[addr + 9] = -tcnv[addr + 9]; 00458 } 00459 00460 rgb[0] = 0.0; 00461 rgb[1] = 0.0; 00462 rgb[2] = 0.0; 00463 00464 // draw the triangle strips 00465 tex=tex_colormaterial(rgb, materialIndex); 00466 rt_tristripscnv3fv(rtscene, tex, numverts, tcnv, 00467 numstrips, vertsperstrip, facets); 00468 00469 delete [] tcnv; 00470 } 00471 00472 00473 00475 00476 int LibTachyonDisplayDevice::open_file(const char *filename) { 00477 my_filename = stringdup(filename); 00478 isOpened = TRUE; 00479 reset_state(); 00480 return TRUE; 00481 } 00482 00483 void LibTachyonDisplayDevice::close_file(void) { 00484 outfile = NULL; 00485 delete [] my_filename; 00486 my_filename = NULL; 00487 isOpened = FALSE; 00488 } 00489 00490 static int checkfileextension(const char * s, const char * extension) { 00491 int sz, extsz; 00492 sz = strlen(s); 00493 extsz = strlen(extension); 00494 00495 if (extsz > sz) 00496 return 0; 00497 00498 if (!strupncmp(s + (sz - extsz), extension, extsz)) { 00499 return 1; 00500 } 00501 00502 return 0; 00503 } 00504 00505 // initialize the file for output 00506 void LibTachyonDisplayDevice::write_header() { 00507 wkf_timer_start(trt_timer); 00508 00509 // NOTE: the vmd variable "Aspect" has absolutely *nothing* to do 00510 // with aspect ratio correction, it is only the ratio of the 00511 // width of the graphics window to its height, and so it should 00512 // be used only to cause the ray tracer to generate a similarly 00513 // proportioned image. 00514 00515 buildtime = rt_timer_create(); 00516 rendertime = rt_timer_create(); 00517 00518 rt_timer_start(buildtime); 00519 00520 #if 0 && defined(VMDMPI) 00521 if (getenv("VMDNOMPI") == NULL || getenv("VMDTACHYONNOMPI") == NULL) { 00522 // Unless overridden, every VMD node runs Tachyon independently of one another, 00523 // with each node in its own communicator... 00524 if (getenv("VMDTACHYONWORKGROUP") == NULL) { 00525 parallel_group = -1; 00526 rt_set_mpi_comm_world_split_all(); 00527 } else { 00528 parallel_group = atoi(getenv("VMDTACHYONWORKGROUP")); 00529 rt_set_mpi_comm_world_split(parallel_group, 0); 00530 } 00531 } 00532 #endif 00533 00534 rtscene = rt_newscene(); 00535 rt_outputfile(rtscene, my_filename); // get filename from base class 00536 00537 switch (curformat) { 00538 case 0: // autodetermine... 00539 // set appropriate image file format 00540 if (checkfileextension(my_filename, ".bmp")) { 00541 rt_outputformat(rtscene, RT_FORMAT_WINBMP); 00542 } else if (checkfileextension(my_filename, ".ppm")) { 00543 rt_outputformat(rtscene, RT_FORMAT_PPM); 00544 } else if (checkfileextension(my_filename, ".psd")) { 00545 rt_outputformat(rtscene, RT_FORMAT_PSD48); 00546 } else if (checkfileextension(my_filename, ".rgb")) { 00547 rt_outputformat(rtscene, RT_FORMAT_SGIRGB); 00548 } else if (checkfileextension(my_filename, ".tga")) { 00549 rt_outputformat(rtscene, RT_FORMAT_TARGA); 00550 } else { 00551 #if defined(_MSC_VER) || defined(WIN32) 00552 msgErr << "Unrecognized image file extension, writing Windows Bitmap file." 00553 << sendmsg; 00554 rt_outputformat(rtscene, RT_FORMAT_WINBMP); 00555 #else 00556 msgErr << "Unrecognized image file extension, writing Targa file." 00557 << sendmsg; 00558 rt_outputformat(rtscene, RT_FORMAT_TARGA); 00559 #endif 00560 } 00561 break; 00562 00563 case 1: 00564 rt_outputformat(rtscene, RT_FORMAT_WINBMP); 00565 break; 00566 00567 case 2: 00568 rt_outputformat(rtscene, RT_FORMAT_PPM); 00569 break; 00570 00571 case 3: 00572 rt_outputformat(rtscene, RT_FORMAT_PPM48); 00573 00574 case 4: 00575 rt_outputformat(rtscene, RT_FORMAT_PSD48); 00576 00577 case 5: 00578 rt_outputformat(rtscene, RT_FORMAT_SGIRGB); 00579 00580 case 6: 00581 default: 00582 rt_outputformat(rtscene, RT_FORMAT_TARGA); 00583 } 00584 00585 00586 rt_resolution(rtscene, (int) xSize, (int) ySize); 00587 00588 // use opacity post-multiply transparency mode for output that closely 00589 // matches what user's see in the OpenGL window in VMD. 00590 rt_trans_mode(rtscene, RT_TRANS_VMD); 00591 00592 // use planar fog mode (rather than the more physically correct radial fog) 00593 // for output that closely matches what user's see in the VMD OpenGL window. 00594 rt_fog_rendering_mode(rtscene, RT_FOG_VMD); 00595 00596 write_camera(); // has to be first thing in the file. 00597 write_lights(); // could be anywhere. 00598 write_materials(); // has to be before objects that use them. 00599 00600 // render with/without shadows 00601 if (shadows_enabled() || ao_enabled()) { 00602 if (shadows_enabled() && !ao_enabled()) 00603 msgInfo << "Shadow rendering enabled." << sendmsg; 00604 00605 rt_shadermode(rtscene, RT_SHADER_FULL); // full shading mode required 00606 } else { 00607 rt_shadermode(rtscene, RT_SHADER_MEDIUM); // disable shadows by default 00608 } 00609 00610 // render with ambient occlusion, but only if shadows are also enabled 00611 if (ao_enabled()) { 00612 apicolor skycol; 00613 skycol.r = get_ao_ambient(); 00614 skycol.g = get_ao_ambient(); 00615 skycol.b = get_ao_ambient(); 00616 00617 msgInfo << "Ambient occlusion rendering enabled." << sendmsg; 00618 rt_rescale_lights(rtscene, get_ao_direct()); 00619 00620 #if ((TACHYON_MAJOR_VERSION == 0) && (TACHYON_MINOR_VERSION == 99) && (TACHYON_PATCH_VERSION <= 1)) 00621 rt_ambient_occlusion(rtscene, aosamples, skycol); 00622 #else 00623 float ao_maxdist = RT_AO_MAXDIST_UNLIMITED; 00624 00625 if (getenv("VMDTACHYONAOMAXDIST")) { 00626 float tmp = atof(getenv("VMDTACHYONAOMAXDIST")); 00627 printf("LibTachyonDisplayDevice) setting AO maxdist: %f\n", tmp); 00628 ao_maxdist = tmp; 00629 } 00630 00631 rt_ambient_occlusion(rtscene, aosamples, ao_maxdist, skycol); 00632 #endif 00633 } 00634 00635 // render with depth of field, but only for perspective projection 00636 if (dof_enabled() && (projection() == DisplayDevice::PERSPECTIVE)) { 00637 msgInfo << "DoF focal blur enabled." << sendmsg; 00638 rt_camera_projection(rtscene, RT_PROJECTION_PERSPECTIVE_DOF); 00639 rt_camera_dof(rtscene, get_dof_focal_dist(), get_dof_fnumber()); 00640 } 00641 } 00642 00643 00644 void LibTachyonDisplayDevice::write_trailer(void){ 00645 rt_timer_stop(buildtime); 00646 rt_timer_start(rendertime); 00647 rt_renderscene(rtscene); 00648 rt_timer_stop(rendertime); 00649 rt_deletescene(rtscene); 00650 00651 msgInfo << "Tachyon: preprocessing time " 00652 << rt_timer_time(buildtime) << " sec, render time " 00653 << rt_timer_time(rendertime) << " sec." << sendmsg; 00654 rt_timer_destroy(buildtime); 00655 rt_timer_destroy(rendertime); 00656 00657 if (inclipgroup) { 00658 msgErr << "LibTachyonDisplayDevice clip group still active at end of scene" << sendmsg; 00659 } 00660 00661 wkf_timer_stop(trt_timer); 00662 printf("Total Tachyon rendering time: %.1f sec\n", wkf_timer_time(trt_timer)); 00663 00664 reset_vars(); // reset internal state between renders 00665 } 00666 00667 00668 // define a volumetric texture map 00669 void LibTachyonDisplayDevice::define_volume_texture(int ID, 00670 int xs, int ys, int zs, 00671 const float *xpq, 00672 const float *ypq, 00673 const float *zpq, 00674 unsigned char *texmap) { 00675 char texname[1024]; 00676 unsigned char *rgb=NULL; 00677 00678 voltexID = ID; // remember current texture ID 00679 00680 // remember texture plane equations 00681 memcpy(xplaneeq, xpq, sizeof(xplaneeq)); 00682 memcpy(yplaneeq, ypq, sizeof(yplaneeq)); 00683 memcpy(zplaneeq, zpq, sizeof(zplaneeq)); 00684 00685 sprintf(texname, "::VMDVolTex%d", voltexID); 00686 00687 // copy incoming texture map to a new buffer 00688 // XXX ideally we'd have Tachyon use the existing image 00689 // buffer rather than copy it 00690 long txsz = long(xs) * long(ys) * long(zs) * 3L; 00691 rgb = (unsigned char *) malloc(txsz); 00692 memcpy(rgb, texmap, txsz); 00693 00694 // define the texture map image within Tachyon 00695 rt_define_teximage_rgb24(texname, xs, ys, zs, rgb); 00696 } 00697 00698 00699 // enable volumetric texturing, either in "replace" or "modulate" mode 00700 void LibTachyonDisplayDevice::volume_texture_on(int texmode) { 00701 involtex = 1; 00702 } 00703 00704 00705 // disable volumetric texturing 00706 void LibTachyonDisplayDevice::volume_texture_off(void) { 00707 involtex = 0; 00708 } 00709 00710 00711 void LibTachyonDisplayDevice::start_clipgroup(void) { 00712 int i; 00713 int planesenabled = 0; 00714 00715 for (i=0; i<VMD_MAX_CLIP_PLANE; i++) { 00716 if (clip_mode[i] > 0) { 00717 planesenabled++; /* count number of active clipping planes */ 00718 if (clip_mode[i] > 1) 00719 warningflags |= FILERENDERER_NOCLIP; /* emit warnings */ 00720 } 00721 } 00722 00723 if (planesenabled > 0) { 00724 float *planes = (float *) malloc(planesenabled * 4 * sizeof(float)); 00725 00726 int j=0; 00727 for (i=0; i<VMD_MAX_CLIP_PLANE; i++) { 00728 if (clip_mode[i] > 0) { 00729 float tachyon_clip_center[3]; 00730 float tachyon_clip_normal[3]; 00731 float tachyon_clip_distance; 00732 00733 inclipgroup = 1; // we're in a clipping group presently 00734 00735 // Transform the plane center and the normal 00736 (transMat.top()).multpoint3d(clip_center[i], tachyon_clip_center); 00737 (transMat.top()).multnorm3d(clip_normal[i], tachyon_clip_normal); 00738 vec_negate(tachyon_clip_normal, tachyon_clip_normal); 00739 00740 // Tachyon uses the distance from the origin to the plane for its 00741 // representation, instead of the plane center 00742 tachyon_clip_distance = dot_prod(tachyon_clip_normal, tachyon_clip_center); 00743 00744 planes[j * 4 ] = tachyon_clip_normal[0]; 00745 planes[j * 4 + 1] = tachyon_clip_normal[1]; 00746 planes[j * 4 + 2] = -tachyon_clip_normal[2]; 00747 planes[j * 4 + 3] = tachyon_clip_distance; 00748 00749 rt_clip_fv(rtscene, planesenabled, planes); // add the clipping planes 00750 j++; 00751 } 00752 } 00753 00754 free(planes); 00755 } else { 00756 inclipgroup = 0; // Not currently in a clipping group 00757 } 00758 } 00759 00760 00761 void LibTachyonDisplayDevice::end_clipgroup(void) { 00762 if (inclipgroup) { 00763 rt_clip_off(rtscene); // disable clipping planes 00764 inclipgroup = 0; // we're not in a clipping group anymore 00765 } 00766 } 00767 00768 00770 00771 void LibTachyonDisplayDevice::write_camera(void) { 00772 int raydepth = 50; 00773 00774 // Camera position 00775 // Tachyon uses a left-handed coordinate system 00776 // VMD uses right-handed, so z(Tachyon) = -z(VMD). 00777 switch (projection()) { 00778 case DisplayDevice::ORTHOGRAPHIC: 00779 rt_camera_projection(rtscene, RT_PROJECTION_ORTHOGRAPHIC); 00780 rt_camera_setup(rtscene, 00781 1.0 / (vSize / 2.0), // zoom 00782 1.0f, // aspect ratio 00783 aasamples, // antialiasing 00784 raydepth, // ray depth 00785 rt_vector(eyePos[0], eyePos[1], -eyePos[2]), // camcent 00786 rt_vector(eyeDir[0], eyeDir[1], -eyeDir[2]), // camview 00787 rt_vector(upDir[0], upDir[1], -upDir[2])); // camup 00788 break; 00789 00790 case DisplayDevice::PERSPECTIVE: 00791 default: 00792 rt_camera_projection(rtscene, RT_PROJECTION_PERSPECTIVE); 00793 rt_camera_setup(rtscene, 00794 ((eyePos[2] - zDist) / vSize), // zoom 00795 1.0f, // aspect ratio 00796 aasamples, // antialiasing 00797 raydepth, // ray depth 00798 rt_vector(eyePos[0], eyePos[1], -eyePos[2]), // camcent 00799 rt_vector(eyeDir[0], eyeDir[1], -eyeDir[2]), // camview 00800 rt_vector(upDir[0], upDir[1], -upDir[2])); // camup 00801 break; 00802 } 00803 } 00804 00805 00806 void LibTachyonDisplayDevice::write_lights(void) { 00807 int i; 00808 int lightcount = 0; 00809 00810 /* directional lights */ 00811 for (i=0; i<DISP_LIGHTS; i++) { 00812 if (lightState[i].on) { 00813 apitexture tex; 00814 memset(&tex, 0, sizeof(apitexture)); 00815 00816 tex.col.r=lightState[i].color[0]; 00817 tex.col.g=lightState[i].color[1]; 00818 tex.col.b=lightState[i].color[2]; 00819 00820 rt_directional_light(rtscene, 00821 rt_texture(rtscene, &tex), 00822 /* give negated light position for direction vector... */ 00823 rt_vector(-lightState[i].pos[0], 00824 -lightState[i].pos[1], 00825 lightState[i].pos[2])); 00826 00827 lightcount++; 00828 } 00829 } 00830 00831 /* advanced positional lights */ 00832 for (i=0; i<DISP_LIGHTS; i++) { 00833 if (advLightState[i].on) { 00834 float pos[3]; 00835 00836 // always use world coordinates for now 00837 vec_copy(pos, advLightState[i].pos); 00838 00839 if (advLightState[i].spoton) { 00840 printf("TachyonInternal) SpotLight not implemented yet ...\n"); 00841 } else { 00842 apitexture tex; 00843 memset(&tex, 0, sizeof(apitexture)); 00844 00845 tex.col.r=advLightState[i].color[0]; 00846 tex.col.g=advLightState[i].color[1]; 00847 tex.col.b=advLightState[i].color[2]; 00848 00849 void *l = rt_light(rtscene, 00850 rt_texture(rtscene, &tex), 00851 /* negate position to correct handedness... */ 00852 rt_vector(pos[0], pos[1], -pos[2]), 0.0f); 00853 00854 /* emit light attentuation parameters if needed */ 00855 if (advLightState[i].constfactor != 1.0f || 00856 advLightState[i].linearfactor != 0.0f || 00857 advLightState[i].quadfactor != 0.0f) { 00858 rt_light_attenuation(l, 00859 advLightState[i].constfactor, 00860 advLightState[i].linearfactor, 00861 advLightState[i].quadfactor); 00862 } 00863 } 00864 00865 lightcount++; 00866 } 00867 } 00868 00869 if (lightcount < 1) { 00870 msgInfo << "Warning: no lights defined in exported scene!!" << sendmsg; 00871 } 00872 } 00873 00874 void LibTachyonDisplayDevice::write_materials(void) { 00875 // background color 00876 apicolor col; 00877 col.r = backColor[0]; 00878 col.g = backColor[1]; 00879 col.b = backColor[2]; 00880 rt_background(rtscene, col); 00881 00882 // Specify Tachyon background sky sphere if background gradient 00883 // mode is enabled. 00884 if (backgroundmode == 1) { 00885 float bspheremag = 0.5f; 00886 00887 // compute positive/negative magnitude of sphere gradient 00888 switch (projection()) { 00889 case DisplayDevice::ORTHOGRAPHIC: 00890 // For orthographic views, Tachyon uses the dot product between 00891 // the incident ray origin and the sky sphere gradient "up" vector, 00892 // since all camera rays have the same direction and differ only 00893 // in their origin. 00894 bspheremag = vSize / 4.0f; 00895 break; 00896 00897 case DisplayDevice::PERSPECTIVE: 00898 default: 00899 // For perspective views, Tachyon uses the dot product between 00900 // the incident ray and the sky sphere gradient "up" vector, 00901 // so for larger values of vSize, we have to clamp the maximum 00902 // magnitude to 1.0. 00903 bspheremag = (vSize / 2.0f) / (eyePos[2] - zDist); 00904 if (bspheremag > 1.0f) 00905 bspheremag = 1.0f; 00906 break; 00907 } 00908 00909 if (projection() == DisplayDevice::ORTHOGRAPHIC) 00910 rt_background_mode(rtscene, RT_BACKGROUND_TEXTURE_SKY_ORTHO_PLANE); 00911 else 00912 rt_background_mode(rtscene, RT_BACKGROUND_TEXTURE_SKY_SPHERE); 00913 rt_background_gradient(rtscene, 00914 rt_vector(0, 1, 0), 00915 bspheremag, -bspheremag, 00916 rt_color(backgradienttopcolor[0], 00917 backgradienttopcolor[1], 00918 backgradienttopcolor[2]), 00919 rt_color(backgradientbotcolor[0], 00920 backgradientbotcolor[1], 00921 backgradientbotcolor[2])); 00922 } 00923 00924 // set depth cueing parameters 00925 if (cueingEnabled) { 00926 switch (cueMode) { 00927 case CUE_LINEAR: 00928 rt_fog_mode(rtscene, RT_FOG_LINEAR); 00929 rt_fog_parms(rtscene, col, get_cue_start(), get_cue_end(), 1.0f); 00930 break; 00931 00932 case CUE_EXP: 00933 rt_fog_mode(rtscene, RT_FOG_EXP); 00934 rt_fog_parms(rtscene, col, 0.0, get_cue_end(), get_cue_density()); 00935 break; 00936 00937 case CUE_EXP2: 00938 rt_fog_mode(rtscene, RT_FOG_EXP2); 00939 rt_fog_parms(rtscene, col, 0.0, get_cue_end(), get_cue_density()); 00940 break; 00941 00942 case NUM_CUE_MODES: 00943 // this should never happen 00944 break; 00945 } 00946 } else { 00947 rt_fog_mode(rtscene, RT_FOG_NONE); 00948 } 00949 } 00950 00951 00952 void * LibTachyonDisplayDevice::tex_cindexmaterial(int cindex, int material) { 00953 float *rgb = (float *) &matData[cindex]; 00954 void *voidtex; 00955 00956 voidtex = tex_colormaterial(rgb, material); 00957 00958 return voidtex; 00959 } 00960 00961 00962 void * LibTachyonDisplayDevice::tex_colormaterial(float *rgb, int material) { 00963 apitexture tex; 00964 void *voidtex; 00965 00966 memset(&tex, 0, sizeof(apitexture)); 00967 00968 if (materials_on) { 00969 tex.ambient = mat_ambient; 00970 tex.diffuse = mat_diffuse; 00971 tex.specular = mat_mirror; 00972 } else { 00973 tex.ambient = 1.0; 00974 tex.diffuse = 0.0; 00975 tex.specular = 0.0; 00976 } 00977 00978 tex.opacity = mat_opacity; 00979 tex.col.r = rgb[0]; 00980 tex.col.g = rgb[1]; 00981 tex.col.b = rgb[2]; 00982 00984 if (!involtex) { 00986 tex.texturefunc = RT_TEXTURE_CONSTANT; 00987 } else { 00991 float voluaxs[3]; 00992 float volvaxs[3]; 00993 float volwaxs[3]; 00994 float volcent[3]; 00995 00996 // transform the y/v/w texture coordinate axes from molecule 00997 // coordinates into world coordinates 00998 (transMat.top()).multplaneeq3d(xplaneeq, voluaxs); 00999 (transMat.top()).multplaneeq3d(yplaneeq, volvaxs); 01000 (transMat.top()).multplaneeq3d(zplaneeq, volwaxs); 01001 01002 // undo the scaling operation applied by the transformation 01003 float invscale = 1.0f / scale_radius(1.0f); 01004 int i; 01005 for (i=0; i<3; i++) { 01006 voluaxs[i] *= invscale; 01007 volvaxs[i] *= invscale; 01008 volwaxs[i] *= invscale; 01009 } 01010 01011 // compute the volume origin in molecule coordinates by 01012 // reverting the scaling factor that was previously applied 01013 // to the texture plane equation 01014 float volorgmol[3] = {0,0,0}; 01015 volorgmol[0] = -xplaneeq[3] / norm(xplaneeq); 01016 volorgmol[1] = -yplaneeq[3] / norm(yplaneeq); 01017 volorgmol[2] = -zplaneeq[3] / norm(zplaneeq); 01018 01019 // transform the volume origin into world coordinates 01020 (transMat.top()).multpoint3d(volorgmol, volcent); 01021 01022 tex.texturefunc = RT_TEXTURE_VOLUME_IMAGE; 01023 01024 sprintf(tex.imap, "::VMDVolTex%d", voltexID); 01025 tex.ctr.x = volcent[0]; 01026 tex.ctr.y = volcent[1]; 01027 tex.ctr.z = -volcent[2]; 01028 tex.rot.x = 0; 01029 tex.rot.y = 0; 01030 tex.rot.z = 0; 01031 tex.scale.x = 1; 01032 tex.scale.y = 1; 01033 tex.scale.z = 1; 01034 tex.uaxs.x = voluaxs[0]; 01035 tex.uaxs.y = voluaxs[1]; 01036 tex.uaxs.z = -voluaxs[2]; 01037 tex.vaxs.x = volvaxs[0]; 01038 tex.vaxs.y = volvaxs[1]; 01039 tex.vaxs.z = -volvaxs[2]; 01040 tex.waxs.x = volwaxs[0]; 01041 tex.waxs.y = volwaxs[1]; 01042 tex.waxs.z = -volwaxs[2]; 01043 } 01044 01046 voidtex=rt_texture(rtscene, &tex); 01047 01049 rt_tex_phong(voidtex, mat_specular, mat_shininess, RT_PHONG_PLASTIC); 01050 01052 if (mat_transmode) 01053 rt_tex_transmode(voidtex, RT_TRANS_RASTER3D); 01054 01056 rt_tex_outline(voidtex, mat_outline, mat_outlinewidth); 01057 01058 return voidtex; 01059 } 01060 01061