00001 /*************************************************************************** 00002 *cr 00003 *cr (C) Copyright 2013-2014 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: ANARIDisplayDevice.C,v $ 00013 * $Author: johns $ $Locker: $ $State: Exp $ 00014 * $Revision: 1.3 $ $Date: 2021年05月14日 22:13:07 $ 00015 * 00016 ***************************************************************************/ 00037 #include <math.h> 00038 #include <stdlib.h> 00039 #include <stdio.h> 00040 #include <string.h> 00041 00042 #include "VMDApp.h" // needed for accelerator memory management 00043 #include "QuickSurf.h" // needed for accelerator memory management 00044 00045 #include "DispCmds.h" // CYLINDER_TRAILINGCAP, etc.. 00046 #include "ANARIDisplayDevice.h" 00047 #include "ANARIRenderer.h" 00048 #include "config.h" // needed for default image viewer 00049 #include "Hershey.h" // needed for Hershey font rendering fctns 00050 00051 00052 // The default radius for points and lines (which are displayed 00053 // as small spheres or cylinders, respectively) 00054 #define DEFAULT_RADIUS 0.0025f 00055 00056 00057 // Global ANARI initialization routine -- call it only ONCE... 00058 void ANARIDisplayDevice::ANARI_Global_Init(void) { 00059 ANARIRender::ANARI_Global_Init(); // call only ONCE 00060 } 00061 00062 // Global ANARI shutdown routine -- call it only ONCE... 00063 void ANARIDisplayDevice::ANARI_Global_Shutdown(void) { 00064 ANARIRender::ANARI_Global_Shutdown(); // call only ONCE 00065 } 00066 00067 00069 ANARIDisplayDevice::ANARIDisplayDevice(VMDApp *app, int interactive) : FileRenderer((interactive) ? 00070 "ANARIInteractive" : "ANARIInternal", 00071 (interactive) ? 00072 "ANARI (interactive)" : "ANARI (internal, in-memory)", 00073 "vmdscene.ppm", DEF_VMDIMAGEVIEWER) { 00074 vmdapp = app; // save VMDApp handle for init or memory management routines 00075 00076 reset_vars(); // initialize material cache 00077 00078 // flag interactive or not 00079 isinteractive = interactive; 00080 00081 // Add supported file formats 00082 formats.add_name("PPM", 0); 00083 00084 // Default image format depends on platform 00085 curformat = 0; 00086 00087 // Set default aa level 00088 has_aa = TRUE; 00089 aasamples = 12; 00090 aosamples = 12; 00091 00092 ort = new ANARIRender(); 00093 ort_timer = wkf_timer_create(); 00094 } 00095 00097 ANARIDisplayDevice::~ANARIDisplayDevice(void) { 00098 delete ort; 00099 ort = NULL; 00100 wkf_timer_destroy(ort_timer); 00101 } 00102 00103 void ANARIDisplayDevice::add_material(void) { 00104 ort->add_material(materialIndex, 00105 mat_ambient, mat_diffuse, mat_specular, mat_shininess, 00106 mat_mirror, mat_opacity, mat_outline, mat_outlinewidth, 00107 mat_transmode > 0.5f); 00108 } 00109 00110 00112 void ANARIDisplayDevice::reset_vars(void) { 00113 // reset_cylinder_buffer(); 00114 reset_triangle_buffer(); 00115 } 00116 00117 00118 void ANARIDisplayDevice::comment(const char *s) { 00119 ort->comment(s); 00120 } 00121 00122 00123 #if 0 00124 00125 void ANARIDisplayDevice::send_cylinder_buffer() { 00126 if (cylinder_vert_buffer.num() > 0) { 00127 // send the cylinders... 00128 ort->cylinder_array_color(*cylinder_xform, cylinder_radius_scalefactor, 00129 cylinder_vert_buffer.num()/6, 00130 &cylinder_vert_buffer[0], 00131 &cylinder_radii_buffer[0], 00132 &cylinder_color_buffer[0], 00133 cylinder_matindex); 00134 00135 #if 0 00136 // send the cylinder end caps, if any 00137 if (cylcap_vert_buffer.num() > 0) { 00138 ort->ring_array_color(*cylinder_xform, cylinder_radius_scalefactor, 00139 cylcap_vert_buffer.num()/3, 00140 &cylcap_vert_buffer[0], 00141 &cylcap_norm_buffer[0], 00142 &cylcap_radii_buffer[0], 00143 &cylcap_color_buffer[0], 00144 cylinder_matindex); 00145 } 00146 #endif 00147 00148 delete cylinder_xform; 00149 cylinder_xform=NULL; 00150 } 00151 // reset_cylinder_buffer(); 00152 } 00153 00154 00155 // draw a cylinder 00156 void ANARIDisplayDevice::cylinder(float *a, float *b, float r, int filled) { 00157 // if we have a change in transformation matrix, color, or material state, 00158 // we have to emit all accumulated cylinders to ANARI and begin a new batch 00159 if (cylinder_xform != NULL && ((cylinder_matindex != materialIndex) || (memcmp(cylinder_xform->mat, transMat.top().mat, sizeof(cylinder_xform->mat))))) { 00160 send_cylinder_buffer(); // render the accumulated cylinder buffer... 00161 } 00162 00163 // record all transformation/material/color state on first cylinder call 00164 if (cylinder_xform == NULL) { 00165 // record material, color, and transformation state 00166 cylinder_matindex = materialIndex; 00167 cylinder_xform = new Matrix4(transMat.top()); 00168 cylinder_radius_scalefactor = scale_factor(); 00169 add_material(); // cause ANARI to cache the current material 00170 } 00171 00172 // record vertex data 00173 cylinder_vert_buffer.append2x3(&a[0], &b[0]); 00174 cylinder_radii_buffer.append(r); 00175 cylinder_color_buffer.append3(&matData[colorIndex][0]); 00176 00177 #if 0 00178 // XXX Cylinder caps not yet supported in ANARI 00179 // Cylinder caps? 00180 if (filled) { 00181 float norm[3]; 00182 norm[0] = b[0] - a[0]; 00183 norm[1] = b[1] - a[1]; 00184 norm[2] = b[2] - a[2]; 00185 00186 float div = 1.0f / sqrtf(norm[0]*norm[0] + norm[1]*norm[1] + norm[2]*norm[2]); 00187 norm[0] *= div; 00188 norm[1] *= div; 00189 norm[2] *= div; 00190 00191 if (filled & CYLINDER_TRAILINGCAP) { 00192 cylcap_vert_buffer.append3(&a[0]); 00193 cylcap_norm_buffer.append3(&norm[0]); 00194 cylcap_radii_buffer.append2(0.0f, r); 00195 cylcap_color_buffer.append3(&matData[colorIndex][0]); 00196 } 00197 00198 if (filled & CYLINDER_LEADINGCAP) { 00199 cylcap_vert_buffer.append3(&b[0]); 00200 norm[0] *= -1; 00201 norm[1] *= -1; 00202 norm[2] *= -1; 00203 cylcap_norm_buffer.append3(&norm[0]); 00204 cylcap_radii_buffer.append2(0.0f, r); 00205 cylcap_color_buffer.append3(&matData[colorIndex][0]); 00206 } 00207 } 00208 #endif 00209 } 00210 00211 #endif 00212 00213 00214 // draw a sphere array 00215 void ANARIDisplayDevice::sphere_array(int spnum, int spres, float *centers, 00216 float *radii, float *colors) { 00217 add_material(); 00218 ort->sphere_array_color(transMat.top(), scale_factor(), spnum, 00219 centers, radii, colors, materialIndex); 00220 00221 // set final color state after array has been drawn 00222 int ind=(spnum-1)*3; 00223 super_set_color(nearest_index(colors[ind], colors[ind+1], colors[ind+2])); 00224 } 00225 00226 00227 #if 0 00228 00229 // 00230 // XXX text needs cylinders to be performant... 00231 // 00232 void ANARIDisplayDevice::text(float *pos, float size, float thickness, 00233 const char *str) { 00234 float textpos[3]; 00235 float textsize, textthickness; 00236 hersheyhandle hh; 00237 00238 // transform the world coordinates 00239 (transMat.top()).multpoint3d(pos, textpos); 00240 textsize = size * 1.5f; 00241 textthickness = thickness*DEFAULT_RADIUS; 00242 00243 ResizeArray<float> text_spheres; 00244 ResizeArray<float> text_cylinders; 00245 00246 while (*str != '0円') { 00247 float lm, rm, x, y, ox, oy; 00248 int draw, odraw; 00249 ox=oy=x=y=0.0f; 00250 draw=odraw=0; 00251 00252 hersheyDrawInitLetter(&hh, *str, &lm, &rm); 00253 textpos[0] -= lm * textsize; 00254 00255 while (!hersheyDrawNextLine(&hh, &draw, &x, &y)) { 00256 float oldpt[3], newpt[3]; 00257 if (draw) { 00258 newpt[0] = textpos[0] + textsize * x; 00259 newpt[1] = textpos[1] + textsize * y; 00260 newpt[2] = textpos[2]; 00261 00262 if (odraw) { 00263 // if we have both previous and next points, connect them... 00264 oldpt[0] = textpos[0] + textsize * ox; 00265 oldpt[1] = textpos[1] + textsize * oy; 00266 oldpt[2] = textpos[2]; 00267 00268 text_cylinders.append2x3(&oldpt[0], &newpt[0]); 00269 text_spheres.append3(&newpt[0]); 00270 } else { 00271 // ...otherwise, just draw the next point 00272 text_spheres.append3(&newpt[0]); 00273 } 00274 } 00275 00276 ox=x; 00277 oy=y; 00278 odraw=draw; 00279 } 00280 textpos[0] += rm * textsize; 00281 00282 str++; 00283 } 00284 00285 add_material(); 00286 // add spheres, which are already in world coordinates 00287 if (text_cylinders.num() > 0) { 00288 ort->cylinder_array(NULL, textthickness, matData[colorIndex], 00289 text_cylinders.num() / 6, &text_cylinders[0], 00290 materialIndex); 00291 } 00292 if (text_spheres.num() > 0) { 00293 ort->sphere_array(NULL, textthickness, matData[colorIndex], 00294 text_spheres.num() / 3, &text_spheres[0], NULL, 00295 materialIndex); 00296 } 00297 } 00298 00299 #endif 00300 00301 00302 void ANARIDisplayDevice::send_triangle_buffer() { 00303 if (triangle_vert_buffer.num() > 0) { 00304 ort->trimesh_n3f_v3f(*triangle_xform, 00305 matData[triangle_cindex], 00306 &triangle_norm_buffer[0], 00307 &triangle_vert_buffer[0], 00308 triangle_vert_buffer.num()/9, 00309 triangle_matindex); 00310 delete triangle_xform; 00311 triangle_xform=NULL; 00312 } 00313 reset_triangle_buffer(); 00314 } 00315 00316 00317 // draw a triangle 00318 void ANARIDisplayDevice::triangle(const float *a, const float *b, const float *c, const float *n1, const float *n2, const float *n3) { 00319 // if we have a change in transformation matrix, color, or material state, 00320 // we have to emit all accumulated triangles to ANARI and begin a new batch 00321 if (triangle_xform != NULL && ((triangle_cindex != colorIndex) || (triangle_matindex != materialIndex) || (memcmp(triangle_xform->mat, transMat.top().mat, sizeof(triangle_xform->mat))))) { 00322 send_triangle_buffer(); // render the accumulated triangle buffer... 00323 } 00324 00325 // record all transformation/material/color state on first triangle call 00326 if (triangle_xform == NULL) { 00327 // record material, color, and transformation state 00328 triangle_cindex = colorIndex; 00329 triangle_matindex = materialIndex; 00330 triangle_xform = new Matrix4(transMat.top()); 00331 add_material(); // cause ANARI to cache the current material 00332 } 00333 00334 // record vertex data 00335 triangle_vert_buffer.append3x3(&a[0], &b[0], &c[0]); 00336 00337 // record normal data 00338 triangle_norm_buffer.append3x3(&n1[0], &n2[0], &n3[0]); 00339 } 00340 00341 00342 // draw a tricolor 00343 void ANARIDisplayDevice::tricolor(const float *a, const float *b, const float *c, 00344 const float *n1, const float *n2, const float *n3, 00345 const float *c1, const float *c2, const float *c3) { 00346 add_material(); 00347 00348 float vnc[27]; 00349 vec_copy(&vnc[ 0], a); 00350 vec_copy(&vnc[ 3], b); 00351 vec_copy(&vnc[ 6], c); 00352 00353 vec_copy(&vnc[ 9], n1); 00354 vec_copy(&vnc[12], n2); 00355 vec_copy(&vnc[15], n3); 00356 00357 vec_copy(&vnc[18], c1); 00358 vec_copy(&vnc[21], c2); 00359 vec_copy(&vnc[24], c3); 00360 00361 ort->tricolor_list(transMat.top(), 1, vnc, materialIndex); 00362 } 00363 00364 00365 void ANARIDisplayDevice::trimesh_c4u_n3b_v3f(unsigned char *c, signed char *n, 00366 float *v, int numfacets) { 00367 add_material(); 00368 ort->trimesh_c4u_n3b_v3f(transMat.top(), c, n, v, numfacets, materialIndex); 00369 } 00370 00371 00372 void ANARIDisplayDevice::trimesh_c4u_n3f_v3f(unsigned char *c, float *n, 00373 float *v, int numfacets) { 00374 add_material(); 00375 ort->trimesh_c4u_n3f_v3f(transMat.top(), c, n, v, numfacets, materialIndex); 00376 } 00377 00378 void ANARIDisplayDevice::trimesh_c4n3v3(int numverts, float * cnv, 00379 int numfacets, int * facets) { 00380 add_material(); 00381 ort->trimesh_c4n3v3(transMat.top(), numverts, cnv, numfacets, facets, 00382 materialIndex); 00383 } 00384 00385 void ANARIDisplayDevice::trimesh_n3b_v3f(signed char *n, float *v, int numfacets) { 00386 add_material(); 00387 ort->trimesh_n3b_v3f(transMat.top(), matData[colorIndex], n, v, numfacets, materialIndex); 00388 } 00389 00390 void ANARIDisplayDevice::trimesh_n3f_v3f(float *n, float *v, int numfacets) { 00391 add_material(); 00392 ort->trimesh_n3f_v3f(transMat.top(), matData[colorIndex], n, v, numfacets, materialIndex); 00393 } 00394 00395 00396 #if 0 00397 void ANARIDisplayDevice::trimesh_n3fopt_v3f(float *n, float *v, int numfacets) { 00398 add_material(); 00399 ort->trimesh_v3f(transMat.top(), matData[colorIndex], v, numfacets, materialIndex); 00400 } 00401 00402 #endif 00403 00404 void ANARIDisplayDevice::tristrip(int numverts, const float * cnv, 00405 int numstrips, const int *vertsperstrip, 00406 const int *facets) { 00407 add_material(); 00408 ort->tristrip(transMat.top(), numverts, cnv, numstrips, vertsperstrip, 00409 facets, materialIndex); 00410 } 00411 00412 00413 00414 void ANARIDisplayDevice::write_lights() { 00415 int i; 00416 int lightcount = 0; 00417 00418 // clear all existing lights before (re)appending the current lights, 00419 // otherwise if the ANARI context is reused, we will crash and burn. 00420 ort->clear_all_lights(); 00421 00422 // directional lights 00423 for (i=0; i<DISP_LIGHTS; i++) { 00424 if (lightState[i].on) { 00425 ort->add_directional_light(lightState[i].pos, lightState[i].color); 00426 lightcount++; 00427 } 00428 } 00429 00430 #if 0 00431 // advanced positional lights 00432 for (i=0; i<DISP_LIGHTS; i++) { 00433 if (advLightState[i].on) { 00434 float pos[3]; 00435 00436 // always use world coordinates for now 00437 vec_copy(pos, advLightState[i].pos); 00438 00439 if (advLightState[i].spoton) { 00440 printf("ANARIDisplayDevice) SpotLight not implemented yet ...\n"); 00441 } else { 00442 apitexture tex; 00443 memset(&tex, 0, sizeof(apitexture)); 00444 00445 tex.col.r=advLightState[i].color[0]; 00446 tex.col.g=advLightState[i].color[1]; 00447 tex.col.b=advLightState[i].color[2]; 00448 00449 void *l = rt_light(rtscene, 00450 rt_texture(rtscene, &tex), 00451 /* negate position to correct handedness... */ 00452 rt_vector(pos[0], pos[1], -pos[2]), 0.0f); 00453 00454 /* emit light attentuation parameters if needed */ 00455 if (advLightState[i].constfactor != 1.0f || 00456 advLightState[i].linearfactor != 0.0f || 00457 advLightState[i].quadfactor != 0.0f) { 00458 rt_light_attenuation(l, 00459 advLightState[i].constfactor, 00460 advLightState[i].linearfactor, 00461 advLightState[i].quadfactor); 00462 } 00463 } 00464 00465 lightcount++; 00466 } 00467 } 00468 #endif 00469 00470 if (lightcount < 1) { 00471 msgWarn << "No lights defined in molecular scene!!" << sendmsg; 00472 } 00473 } 00474 00475 00476 void ANARIDisplayDevice::write_materials() { 00477 ort->set_bg_color(backColor); 00478 00479 // Specify ANARI background sky sphere if background gradient 00480 // mode is enabled. 00481 if (backgroundmode == 1) { 00482 float bspheremag = 0.5f; 00483 00484 // compute positive/negative magnitude of sphere gradient 00485 switch (projection()) { 00486 case DisplayDevice::ORTHOGRAPHIC: 00487 // For orthographic views, Tachyon uses the dot product between 00488 // the incident ray origin and the sky sphere gradient "up" vector, 00489 // since all camera rays have the same direction and differ only 00490 // in their origin. 00491 bspheremag = vSize / 4.0f; 00492 break; 00493 00494 case DisplayDevice::PERSPECTIVE: 00495 default: 00496 // For perspective views, Tachyon uses the dot product between 00497 // the incident ray and the sky sphere gradient "up" vector, 00498 // so for larger values of vSize, we have to clamp the maximum 00499 // magnitude to 1.0. 00500 bspheremag = (vSize / 2.0f) / (eyePos[2] - zDist); 00501 if (bspheremag > 1.0f) 00502 bspheremag = 1.0f; 00503 break; 00504 } 00505 00506 if (projection() == DisplayDevice::ORTHOGRAPHIC) 00507 ort->set_bg_mode(ANARIRender::RT_BACKGROUND_TEXTURE_SKY_ORTHO_PLANE); 00508 else 00509 ort->set_bg_mode(ANARIRender::RT_BACKGROUND_TEXTURE_SKY_SPHERE); 00510 00511 float updir[3] = { 0.0f, 1.0f, 0.0f }; 00512 ort->set_bg_color_grad_top(backgradienttopcolor); 00513 ort->set_bg_color_grad_bot(backgradientbotcolor); 00514 ort->set_bg_gradient(updir); 00515 ort->set_bg_gradient_topval(bspheremag); 00516 ort->set_bg_gradient_botval(-bspheremag); 00517 } else { 00518 ort->set_bg_mode(ANARIRender::RT_BACKGROUND_TEXTURE_SOLID); 00519 } 00520 } 00521 00522 00524 00525 void ANARIDisplayDevice::write_header() { 00526 wkf_timer_start(ort_timer); 00527 00528 ort->setup_context(xSize, ySize); 00529 write_materials(); 00530 write_lights(); 00531 00532 ort->set_aa_samples(aasamples); // set with current FileRenderer values 00533 00534 // render with/without shadows 00535 if (shadows_enabled() || ao_enabled()) { 00536 if (shadows_enabled() && !ao_enabled()) 00537 msgInfo << "Shadow rendering enabled." << sendmsg; 00538 00539 ort->shadows_on(1); // shadowing mode required 00540 } else { 00541 ort->shadows_on(0); // disable shadows by default 00542 } 00543 00544 // render with ambient occlusion, but only if shadows are also enabled 00545 if (ao_enabled()) { 00546 msgInfo << "Ambient occlusion enabled." << sendmsg; 00547 msgInfo << "Shadow rendering enabled." << sendmsg; 00548 ort->set_ao_samples(aosamples); // set with current FileRenderer values 00549 } else { 00550 ort->set_ao_samples(0); // disable AO rendering entirely 00551 } 00552 00553 // Always set the AO parameters, that way the user can enable/disable 00554 // AO on-the-fly in the interactive renderer 00555 ort->set_ao_ambient(get_ao_ambient()); 00556 ort->set_ao_direct(get_ao_direct()); 00557 00558 // render with depth of field, but only for perspective projection 00559 if (dof_enabled()) { 00560 msgInfo << "DoF focal blur enabled." << sendmsg; 00561 ort->dof_on(1); // enable DoF rendering 00562 ort->set_camera_dof_fnumber(get_dof_fnumber()); 00563 ort->set_camera_dof_focal_dist(get_dof_focal_dist()); 00564 } else { 00565 ort->dof_on(0); // disable DoF rendering 00566 } 00567 00568 // set depth cueing parameters 00569 float start = get_cue_start(); 00570 float end = get_cue_end(); 00571 float density = get_cue_density(); 00572 if (cueingEnabled) { 00573 switch (cueMode) { 00574 case CUE_LINEAR: 00575 ort->set_cue_mode(ANARIRender::RT_FOG_LINEAR, start, end, density); 00576 break; 00577 00578 case CUE_EXP: 00579 ort->set_cue_mode(ANARIRender::RT_FOG_EXP, start, end, density); 00580 break; 00581 00582 case CUE_EXP2: 00583 ort->set_cue_mode(ANARIRender::RT_FOG_EXP2, start, end, density); 00584 break; 00585 00586 case NUM_CUE_MODES: 00587 // this should never happen 00588 break; 00589 } 00590 } else { 00591 ort->set_cue_mode(ANARIRender::RT_FOG_NONE, start, end, density); 00592 } 00593 } 00594 00595 00596 void ANARIDisplayDevice::write_trailer(void){ 00597 // send_cylinder_buffer(); // send any unsent accumulated cylinder buffer... 00598 send_triangle_buffer(); // send any unsent accumulated triangle buffer... 00599 00600 #if 0 00601 printf("ANARI: z: %f zDist: %f vSize %f\n", eyePos[2], zDist, vSize); 00602 #endif 00603 switch (projection()) { 00604 case DisplayDevice::ORTHOGRAPHIC: 00605 ort->set_camera_projection(ANARIRender::RT_ORTHOGRAPHIC); 00606 ort->set_camera_zoom(0.5f / (1.0 / (vSize / 2.0))); 00607 break; 00608 00609 case DisplayDevice::PERSPECTIVE: 00610 default: 00611 ort->set_camera_projection(ANARIRender::RT_PERSPECTIVE); 00612 // ort->set_camera_zoom(0.5f / ((eyePos[2] - zDist) / vSize)); 00613 ort->set_camera_zoom(0.5f * vSize / (eyePos[2] - zDist)); 00614 } 00615 00616 // set stereoscopic display parameters 00617 ort->set_camera_stereo_eyesep(eyeSep); 00618 ort->set_camera_stereo_convergence_dist(eyeDist); 00619 00620 char *verbstr = getenv("VMDANARIVERBOSE"); 00621 if (verbstr != NULL) { 00622 if (!strupcmp(verbstr, "TIMING") || !strupcmp(verbstr, "DEBUG")) { 00623 double time_scene_graph = wkf_timer_timenow(ort_timer); 00624 printf("ANARIDisplayDevice) scene graph construction time %.2f\n", 00625 time_scene_graph); 00626 } 00627 } 00628 00629 #if defined(VMDANARI_INTERACTIVE_OPENGL) 00630 if (isinteractive) 00631 ort->render_to_glwin(my_filename); // interactive progressive ray tracer 00632 else 00633 #endif 00634 ort->render_to_file(my_filename); // render the scene in batch mode... 00635 00636 reset_vars(); 00637 00638 if (getenv("VMDANARINODESTROYCONTEXT") == NULL) { 00639 // destroy the current context, because we haven't done enough to ensure 00640 // that we're managing memory well without tearing it all down. 00641 delete ort; 00642 00643 // make a new ANARIRender object so we're ready for the next run... 00644 ort = new ANARIRender(); 00645 } else { 00646 // reset internal state between renders 00647 // reinitialize material cache, clean context state 00648 ort->destroy_scene(); 00649 } 00650 00651 wkf_timer_stop(ort_timer); 00652 printf("ANARIDisplayDevice) Total rendering time: %.2f sec\n", wkf_timer_time(ort_timer)); 00653 } 00654 00655 00656 00657 00658