source: trunk/packages/vizservers/nanovis/nanovis.cpp @ 3900

Last change on this file since 3900 was 3900, checked in by ldelgass, 6 years ago

Exclude grid from scene bounds in nanovis, so camera reset and center of
rotation are based on data bounds (grid can be off center with respect to the
data bounds because grid extends beyond data bounds to end on a major tick).

  • Property svn:eol-style set to native
File size: 26.2 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * ----------------------------------------------------------------------
4 * Nanovis: Visualization of Nanoelectronics Data
5 *
6 * ======================================================================
7 *  AUTHOR:  Wei Qiao <qiaow@purdue.edu>
8 *           Michael McLennan <mmclennan@purdue.edu>
9 *           Purdue Rendering and Perceptualization Lab (PURPL)
10 *
11 *  Copyright (c) 2004-2013  HUBzero Foundation, LLC
12 *
13 *  See the file "license.terms" for information on usage and
14 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 * ======================================================================
16 */
17
18#include <sys/time.h>
19#include <cassert>
20#include <cstdio>
21
22#include <GL/glew.h>
23#include <GL/glut.h>
24
25#include <graphics/RenderContext.h>
26#include <vrmath/BBox.h>
27#include <vrmath/Vector3f.h>
28
29#include <util/FilePath.h>
30#include <util/Fonts.h>
31
32#include <BMPImageLoaderImpl.h>
33#include <ImageLoaderFactory.h>
34
35#include "config.h"
36#include "nanovis.h"
37#include "nanovisServer.h"
38#include "define.h"
39
40#include "Flow.h"
41#include "Grid.h"
42#include "HeightMap.h"
43#include "Camera.h"
44#include "LIC.h"
45#include "Shader.h"
46#include "OrientationIndicator.h"
47#include "PlaneRenderer.h"
48#include "PPMWriter.h"
49#include "Texture2D.h"
50#include "Trace.h"
51#include "TransferFunction.h"
52#include "Unirect.h"
53#include "VelocityArrowsSlice.h"
54#include "Volume.h"
55#include "VolumeInterpolator.h"
56#include "VolumeRenderer.h"
57
58using namespace nv;
59using namespace nv::graphics;
60using namespace nv::util;
61using namespace vrmath;
62
63// STATIC MEMBER DATA
64
65Tcl_Interp *NanoVis::interp = NULL;
66
67bool NanoVis::redrawPending = false;
68bool NanoVis::debugFlag = false;
69
70int NanoVis::winWidth = NPIX;
71int NanoVis::winHeight = NPIX;
72int NanoVis::renderWindow = 0;
73unsigned char *NanoVis::screenBuffer = NULL;
74Texture2D *NanoVis::legendTexture = NULL;
75Fonts *NanoVis::fonts;
76Camera *NanoVis::_camera = NULL;
77RenderContext *NanoVis::renderContext = NULL;
78
79NanoVis::TransferFunctionHashmap NanoVis::tfTable;
80NanoVis::VolumeHashmap NanoVis::volumeTable;
81NanoVis::FlowHashmap NanoVis::flowTable;
82NanoVis::HeightMapHashmap NanoVis::heightMapTable;
83
84BBox NanoVis::sceneBounds;
85
86VolumeRenderer *NanoVis::volRenderer = NULL;
87VelocityArrowsSlice *NanoVis::velocityArrowsSlice = NULL;
88LIC *NanoVis::licRenderer = NULL;
89PlaneRenderer *NanoVis::planeRenderer = NULL;
90OrientationIndicator *NanoVis::orientationIndicator = NULL;
91Grid *NanoVis::grid = NULL;
92
93//frame buffer for final rendering
94GLuint NanoVis::_finalFbo = 0;
95GLuint NanoVis::_finalColorTex = 0;
96GLuint NanoVis::_finalDepthRb = 0;
97
98void
99NanoVis::removeAllData()
100{
101    TRACE("Enter");
102
103    if (grid != NULL) {
104        TRACE("Deleting grid");
105        delete grid;
106    }
107    if (_camera != NULL) {
108        TRACE("Deleting camera");
109        delete _camera;
110    }
111    if (volRenderer != NULL) {
112        TRACE("Deleting volRenderer");
113        delete volRenderer;
114    }
115    if (planeRenderer != NULL) {
116        TRACE("Deleting planeRenderer");
117        delete planeRenderer;
118    }
119    if (legendTexture != NULL) {
120        TRACE("Deleting legendTexture");
121        delete legendTexture;
122    }
123    TRACE("Deleting flows");
124    deleteFlows(interp);
125    if (licRenderer != NULL) {
126        TRACE("Deleting licRenderer");
127        delete licRenderer;
128    }
129    if (velocityArrowsSlice != NULL) {
130        TRACE("Deleting velocityArrowsSlice");
131        delete velocityArrowsSlice;
132    }
133    if (renderContext != NULL) {
134        TRACE("Deleting renderContext");
135        delete renderContext;
136    }
137    if (screenBuffer != NULL) {
138        TRACE("Deleting screenBuffer");
139        delete [] screenBuffer;
140        screenBuffer = NULL;
141    }
142    if (fonts != NULL) {
143        TRACE("Deleting fonts");
144        delete fonts;
145    }
146    TRACE("Leave");
147}
148
149void 
150NanoVis::eventuallyRedraw()
151{
152    if (!redrawPending) {
153        glutPostRedisplay();
154        redrawPending = true;
155    }
156}
157
158void
159NanoVis::panCamera(float dx, float dy)
160{
161    _camera->pan(dx, dy);
162
163    collectBounds();
164    _camera->resetClippingRange(sceneBounds.min, sceneBounds.max);
165}
166
167void
168NanoVis::zoomCamera(float z)
169{
170    _camera->zoom(z);
171
172    collectBounds();
173    _camera->resetClippingRange(sceneBounds.min, sceneBounds.max);
174}
175
176void
177NanoVis::orientCamera(double *quat)
178{
179    _camera->orient(quat);
180
181    collectBounds();
182    _camera->resetClippingRange(sceneBounds.min, sceneBounds.max);
183}
184
185void
186NanoVis::setCameraPosition(Vector3f pos)
187{
188    _camera->setPosition(pos);
189
190    collectBounds();
191    _camera->resetClippingRange(sceneBounds.min, sceneBounds.max);
192}
193
194void
195NanoVis::setCameraUpdir(Camera::AxisDirection dir)
196{
197    _camera->setUpdir(dir);
198
199    collectBounds();
200    _camera->resetClippingRange(sceneBounds.min, sceneBounds.max);
201}
202
203void
204NanoVis::resetCamera(bool resetOrientation)
205{
206    TRACE("Resetting all=%d", resetOrientation ? 1 : 0);
207
208    collectBounds();
209    _camera->reset(sceneBounds.min, sceneBounds.max, resetOrientation);
210}
211
212/** \brief Load a 3D volume
213 *
214 * \param name Volume ID
215 * \param width Number of samples in X direction
216 * \param height Number of samples in Y direction
217 * \param depth Number of samples in Z direction
218 * \param numComponents the number of scalars for each space point. All component
219 * scalars for a point are placed consequtively in data array
220 * width, height and depth: number of points in each dimension
221 * \param data Array of floats
222 * \param vmin Min value of field
223 * \param vmax Max value of field
224 * \param nonZeroMin Minimum non-zero value of field
225 * \param data pointer to an array of floats.
226 */
227Volume *
228NanoVis::loadVolume(const char *name, int width, int height, int depth, 
229                    int numComponents, float *data, double vmin, double vmax, 
230                    double nonZeroMin)
231{
232    VolumeHashmap::iterator itr = volumeTable.find(name);
233    if (itr != volumeTable.end()) {
234        WARN("volume \"%s\" already exists", name);
235        removeVolume(itr->second);
236    }
237
238    Volume *volume = new Volume(width, height, depth,
239                                numComponents,
240                                data, vmin, vmax, nonZeroMin);
241    Volume::updatePending = true;
242    volume->name(name);
243    volumeTable[name] = volume;
244
245    return volume;
246}
247
248// Gets a colormap 1D texture by name.
249TransferFunction *
250NanoVis::getTransferFunction(const TransferFunctionId& id) 
251{
252    TransferFunctionHashmap::iterator itr = tfTable.find(id);
253    if (itr == tfTable.end()) {
254        TRACE("No transfer function named \"%s\" found", id.c_str());
255        return NULL;
256    } else {
257        return itr->second;
258    }
259}
260
261// Creates of updates a colormap 1D texture by name.
262TransferFunction *
263NanoVis::defineTransferFunction(const TransferFunctionId& id,
264                                size_t n, float *data)
265{
266    TransferFunction *tf = getTransferFunction(id);
267    if (tf == NULL) {
268        TRACE("Creating new transfer function \"%s\"", id.c_str());
269        tf = new TransferFunction(id.c_str(), n, data);
270        tfTable[id] = tf;
271    } else {
272        TRACE("Updating existing transfer function \"%s\"", id.c_str());
273        tf->update(n, data);
274    }
275    return tf;
276}
277
278int
279NanoVis::renderLegend(TransferFunction *tf, double min, double max,
280                      int width, int height, const char *volArg)
281{
282    TRACE("Enter");
283
284    int old_width = winWidth;
285    int old_height = winHeight;
286
287    planeRenderer->setScreenSize(width, height);
288    resizeOffscreenBuffer(width, height);
289
290    // generate data for the legend
291    float data[512];
292    for (int i=0; i < 256; i++) {
293        data[i] = data[i+256] = (float)(i/255.0);
294    }
295    legendTexture = new Texture2D(256, 2, GL_FLOAT, GL_LINEAR, 1, data);
296    int index = planeRenderer->addPlane(legendTexture, tf);
297    planeRenderer->setActivePlane(index);
298
299    bindOffscreenBuffer();
300    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear screen
301    planeRenderer->render();
302
303    glPixelStorei(GL_PACK_ALIGNMENT, 1);
304    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, screenBuffer);
305    {
306        char prefix[200];
307
308        TRACE("Sending ppm legend image %s min:%g max:%g", volArg, min, max);
309        sprintf(prefix, "nv>legend %s %g %g", volArg, min, max);
310#ifdef USE_THREADS
311        queuePPM(g_queue, prefix, screenBuffer, width, height);
312#else
313        writePPM(g_fdOut, prefix, screenBuffer, width, height);
314#endif
315    }
316    planeRenderer->removePlane(index);
317    resizeOffscreenBuffer(old_width, old_height);
318
319    delete legendTexture;
320    legendTexture = NULL;
321    TRACE("Leave");
322    return TCL_OK;
323}
324
325//initialize frame buffer objects for offscreen rendering
326bool
327NanoVis::initOffscreenBuffer()
328{
329    TRACE("Enter");
330    assert(_finalFbo == 0);
331    // Initialize a fbo for final display.
332    glGenFramebuffersEXT(1, &_finalFbo);
333
334    glGenTextures(1, &_finalColorTex);
335    glBindTexture(GL_TEXTURE_2D, _finalColorTex);
336
337    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
338    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
339    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
340
341#if defined(HAVE_FLOAT_TEXTURES) && defined(USE_HALF_FLOAT)
342    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, winWidth, winHeight, 0,
343                 GL_RGB, GL_INT, NULL);
344#elif defined(HAVE_FLOAT_TEXTURES)
345    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, winWidth, winHeight, 0,
346                 GL_RGB, GL_INT, NULL);
347#else
348    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, winWidth, winHeight, 0,
349                 GL_RGB, GL_INT, NULL);
350#endif
351
352    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _finalFbo);
353    glGenRenderbuffersEXT(1, &_finalDepthRb);
354    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
355    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, 
356                             winWidth, winHeight);
357    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
358                              GL_TEXTURE_2D, _finalColorTex, 0);
359    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
360                                 GL_RENDERBUFFER_EXT, _finalDepthRb);
361
362    GLenum status;
363    if (!CheckFBO(&status)) {
364        PrintFBOStatus(status, "finalFbo");
365        return false;
366    }
367
368    TRACE("Leave");
369    return true;
370}
371
372//resize the offscreen buffer
373bool
374NanoVis::resizeOffscreenBuffer(int w, int h)
375{
376    TRACE("Enter (%d, %d)", w, h);
377    if ((w == winWidth) && (h == winHeight)) {
378        return true;
379    }
380    winWidth = w;
381    winHeight = h;
382
383    if (fonts) {
384        fonts->resize(w, h);
385    }
386    TRACE("screenBuffer size: %d %d", w, h);
387
388    if (screenBuffer != NULL) {
389        delete [] screenBuffer;
390        screenBuffer = NULL;
391    }
392
393    screenBuffer = new unsigned char[3*winWidth*winHeight];
394    assert(screenBuffer != NULL);
395
396    //delete the current render buffer resources
397    glDeleteTextures(1, &_finalColorTex);
398    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
399    glDeleteRenderbuffersEXT(1, &_finalDepthRb);
400
401    TRACE("before deleteframebuffers");
402    glDeleteFramebuffersEXT(1, &_finalFbo);
403
404    TRACE("reinitialize FBO");
405    //Reinitialize final fbo for final display
406    glGenFramebuffersEXT(1, &_finalFbo);
407
408    glGenTextures(1, &_finalColorTex);
409    glBindTexture(GL_TEXTURE_2D, _finalColorTex);
410
411    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
412    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
413    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
414
415#if defined(HAVE_FLOAT_TEXTURES) && defined(USE_HALF_FLOAT)
416    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, winWidth, winHeight, 0,
417                 GL_RGB, GL_INT, NULL);
418#elif defined(HAVE_FLOAT_TEXTURES)
419    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, winWidth, winHeight, 0,
420                 GL_RGB, GL_INT, NULL);
421#else
422    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, winWidth, winHeight, 0,
423                 GL_RGB, GL_INT, NULL);
424#endif
425
426    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _finalFbo);
427    glGenRenderbuffersEXT(1, &_finalDepthRb);
428    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
429    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, 
430                             winWidth, winHeight);
431    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
432                              GL_TEXTURE_2D, _finalColorTex, 0);
433    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
434                                 GL_RENDERBUFFER_EXT, _finalDepthRb);
435
436    GLenum status;
437    if (!CheckFBO(&status)) {
438        PrintFBOStatus(status, "finalFbo");
439        return false;
440    }
441
442    TRACE("change camera");
443    //change the camera setting
444    _camera->setViewport(0, 0, winWidth, winHeight);
445    planeRenderer->setScreenSize(winWidth, winHeight);
446
447    TRACE("Leave (%d, %d)", w, h);
448    return true;
449}
450
451bool NanoVis::init(const char* path)
452{
453    // print OpenGL driver information
454    TRACE("-----------------------------------------------------------");
455    TRACE("OpenGL version: %s", glGetString(GL_VERSION));
456    TRACE("OpenGL vendor: %s", glGetString(GL_VENDOR));
457    TRACE("OpenGL renderer: %s", glGetString(GL_RENDERER));
458    TRACE("-----------------------------------------------------------");
459
460    if (path == NULL) {
461        ERROR("No path defined for shaders or resources");
462        return false;
463    }
464    GLenum err = glewInit();
465    if (GLEW_OK != err) {
466        ERROR("Can't init GLEW: %s", glewGetErrorString(err));
467        return false;
468    }
469    TRACE("Using GLEW %s", glewGetString(GLEW_VERSION));
470
471    // OpenGL 2.1 includes VBOs, PBOs, MRT, NPOT textures, point parameters, point sprites,
472    // GLSL 1.2, and occlusion queries.
473    if (!GLEW_VERSION_2_1) {
474        ERROR("OpenGL version 2.1 or greater is required");
475        return false;
476    }
477
478    // NVIDIA driver may report OpenGL 2.1, but not support PBOs in
479    // indirect GLX contexts
480    if (!GLEW_ARB_pixel_buffer_object) {
481        ERROR("Pixel buffer objects are not supported by driver, please check that the user running nanovis has permissions to create a direct rendering context (e.g. user has read/write access to /dev/nvidia* device nodes in Linux).");
482        return false;
483    }
484
485    // Additional extensions not in 2.1 core
486
487    // Framebuffer objects were promoted in 3.0
488    if (!GLEW_EXT_framebuffer_object) {
489        ERROR("EXT_framebuffer_oject extension is required");
490        return false;
491    }
492    // Rectangle textures were promoted in 3.1
493    // FIXME: can use NPOT in place of rectangle textures
494    if (!GLEW_ARB_texture_rectangle) {
495        ERROR("ARB_texture_rectangle extension is required");
496        return false;
497    }
498#ifdef HAVE_FLOAT_TEXTURES
499    // Float color buffers and textures were promoted in 3.0
500    if (!GLEW_ARB_texture_float ||
501        !GLEW_ARB_color_buffer_float) {
502        ERROR("ARB_texture_float and ARB_color_buffer_float extensions are required");
503        return false;
504    }
505#endif
506    // FIXME: should use GLSL for portability
507#ifdef USE_ARB_PROGRAMS
508    if (!GLEW_ARB_vertex_program ||
509        !GLEW_ARB_fragment_program) {
510        ERROR("ARB_vertex_program and ARB_fragment_program extensions are required");
511        return false;
512    }
513#else
514    if (!GLEW_NV_vertex_program3 ||
515        !GLEW_NV_fragment_program2) {
516        ERROR("NV_vertex_program3 and NV_fragment_program2 extensions are required");
517        return false;
518    }
519#endif
520
521    if (!FilePath::getInstance()->setPath(path)) {
522        ERROR("can't set file path to %s", path);
523        return false;
524    }
525
526    ImageLoaderFactory::getInstance()->addLoaderImpl("bmp", new BMPImageLoaderImpl());
527
528    return true;
529}
530
531bool
532NanoVis::initGL()
533{
534    TRACE("in initGL");
535    //buffer to store data read from the screen
536    if (screenBuffer) {
537        delete[] screenBuffer;
538        screenBuffer = NULL;
539    }
540    screenBuffer = new unsigned char[3*winWidth*winHeight];
541    assert(screenBuffer != NULL);
542
543    //create the camera with default setting
544    _camera = new Camera(0, 0, winWidth, winHeight);
545
546    glEnable(GL_TEXTURE_2D);
547    glShadeModel(GL_FLAT);
548    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
549    glClearColor(0, 0, 0, 1);
550    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
551
552    //initialize lighting
553    GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
554    GLfloat mat_shininess[] = {30.0};
555    GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};
556
557    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
558    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
559    glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
560    glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
561    glLightfv(GL_LIGHT1, GL_DIFFUSE, white_light);
562    glLightfv(GL_LIGHT1, GL_SPECULAR, white_light);
563
564    //frame buffer object for offscreen rendering
565    if (!initOffscreenBuffer()) {
566        return false;
567    }
568
569    fonts = new Fonts();
570    fonts->addFont("verdana", "verdana.fnt");
571    fonts->setFont("verdana");
572
573    renderContext = new RenderContext();
574
575    volRenderer = new VolumeRenderer();
576
577    planeRenderer = new PlaneRenderer(winWidth, winHeight);
578
579    velocityArrowsSlice = new VelocityArrowsSlice();
580
581    licRenderer = new LIC();
582
583    orientationIndicator = new OrientationIndicator();
584
585    grid = new Grid();
586    grid->setFont(fonts);
587
588    //assert(glGetError()==0);
589
590    TRACE("leaving initGL");
591    return true;
592}
593
594void NanoVis::update()
595{
596    VolumeInterpolator *volInterp = volRenderer->getVolumeInterpolator();
597    if (volInterp->isStarted()) {
598        struct timeval clock;
599        gettimeofday(&clock, NULL);
600        double elapsed_time;
601
602        elapsed_time = clock.tv_sec + clock.tv_usec/1000000.0 -
603            volInterp->getStartTime();
604
605        TRACE("%lf %lf", elapsed_time, 
606              volInterp->getInterval());
607        float fraction;
608        float f;
609
610        f = fmod((float) elapsed_time, (float)volInterp->getInterval());
611        if (f == 0.0) {
612            fraction = 0.0f;
613        } else {
614            fraction = f / volInterp->getInterval();
615        }
616        TRACE("fraction : %f", fraction);
617        volInterp->update(fraction);
618    }
619}
620
621void
622NanoVis::setVolumeRanges()
623{
624    TRACE("Enter");
625
626    double valueMin = DBL_MAX, valueMax = -DBL_MAX;
627    VolumeHashmap::iterator itr;
628    for (itr = volumeTable.begin();
629         itr != volumeTable.end(); ++itr) {
630        Volume *volume = itr->second;
631        if (valueMin > volume->wAxis.min()) {
632            valueMin = volume->wAxis.min();
633        }
634        if (valueMax < volume->wAxis.max()) {
635            valueMax = volume->wAxis.max();
636        }
637    }
638    if ((valueMin < DBL_MAX) && (valueMax > -DBL_MAX)) {
639        Volume::valueMin = valueMin;
640        Volume::valueMax = valueMax;
641    }
642    Volume::updatePending = false;
643    TRACE("Leave");
644}
645
646void
647NanoVis::setHeightmapRanges()
648{
649    TRACE("Enter");
650
651    double valueMin = DBL_MAX, valueMax = -DBL_MAX;
652    HeightMapHashmap::iterator itr;
653    for (itr = heightMapTable.begin();
654         itr != heightMapTable.end(); ++itr) {
655        HeightMap *heightMap = itr->second;
656        if (valueMin > heightMap->wAxis.min()) {
657            valueMin = heightMap->wAxis.min();
658        }
659        if (valueMax < heightMap->wAxis.max()) {
660            valueMax = heightMap->wAxis.max();
661        }
662    }
663    if ((valueMin < DBL_MAX) && (valueMax > -DBL_MAX)) {
664        HeightMap::valueMin = valueMin;
665        HeightMap::valueMax = valueMax;
666    }
667    for (HeightMapHashmap::iterator itr = heightMapTable.begin();
668         itr != heightMapTable.end(); ++itr) {
669        itr->second->mapToGrid(grid);
670    }
671    HeightMap::updatePending = false;
672    TRACE("Leave");
673}
674
675void
676NanoVis::collectBounds(bool onlyVisible)
677{
678    if (Flow::updatePending) {
679        mapFlows();
680    }
681
682    sceneBounds.makeEmpty();
683
684    for (VolumeHashmap::iterator itr = volumeTable.begin();
685         itr != volumeTable.end(); ++itr) {
686        Volume *volume = itr->second;
687
688        if (onlyVisible && !volume->visible())
689            continue;
690
691        BBox bbox;
692        volume->getBounds(bbox.min, bbox.max);
693        sceneBounds.extend(bbox);
694    }
695
696    for (HeightMapHashmap::iterator itr = heightMapTable.begin();
697         itr != heightMapTable.end(); ++itr) {
698        HeightMap *heightMap = itr->second;
699
700        if (onlyVisible && !heightMap->isVisible())
701            continue;
702
703        BBox bbox;
704        heightMap->getBounds(bbox.min, bbox.max);
705        sceneBounds.extend(bbox);
706    }
707
708    {
709        BBox bbox;
710        getFlowBounds(bbox.min, bbox.max, onlyVisible);
711        sceneBounds.extend(bbox);
712    }
713
714    if (!sceneBounds.isEmptyX()) {
715        grid->xAxis.setScale(sceneBounds.min.x, sceneBounds.max.x);
716    }
717    if (!sceneBounds.isEmptyY()) {
718        grid->yAxis.setScale(sceneBounds.min.y, sceneBounds.max.y);
719    }
720    if (!sceneBounds.isEmptyZ()) {
721        grid->zAxis.setScale(sceneBounds.min.z, sceneBounds.max.z);
722    }
723
724#if 0
725    if (!onlyVisible || grid->isVisible()) {
726        BBox bbox;
727        grid->getBounds(bbox.min, bbox.max);
728        sceneBounds.extend(bbox);
729    }
730#endif
731
732    if (sceneBounds.isEmpty()) {
733        sceneBounds.min.set(-0.5, -0.5, -0.5);
734        sceneBounds.max.set( 0.5,  0.5,  0.5);
735    }
736
737    TRACE("Scene bounds: (%g,%g,%g) - (%g,%g,%g)",
738          sceneBounds.min.x, sceneBounds.min.y, sceneBounds.min.z,
739          sceneBounds.max.x, sceneBounds.max.y, sceneBounds.max.z);
740}
741
742void
743NanoVis::setBgColor(float color[3])
744{
745    TRACE("Setting bgcolor to %g %g %g", color[0], color[1], color[2]);
746    glClearColor(color[0], color[1], color[2], 1);
747}
748
749void 
750NanoVis::removeVolume(Volume *volume)
751{
752    VolumeHashmap::iterator itr = volumeTable.find(volume->name());
753    if (itr != volumeTable.end()) {
754        volumeTable.erase(itr);
755    }
756    delete volume;
757}
758
759Flow *
760NanoVis::getFlow(const char *name)
761{
762    FlowHashmap::iterator itr = flowTable.find(name);
763    if (itr == flowTable.end()) {
764        TRACE("Can't find flow '%s'", name);
765        return NULL;
766    }
767    return itr->second;
768}
769
770Flow *
771NanoVis::createFlow(Tcl_Interp *interp, const char *name)
772{
773    FlowHashmap::iterator itr = flowTable.find(name);
774    if (itr != flowTable.end()) {
775        ERROR("Flow '%s' already exists", name);
776        return NULL;
777    }
778    Flow *flow = new Flow(interp, name);
779    flowTable[name] = flow;
780    return flow;
781}
782
783/**
784 * \brief Delete flow object and hash table entry
785 *
786 * This is called by the flow command instance delete callback
787 */
788void
789NanoVis::deleteFlow(const char *name)
790{
791    FlowHashmap::iterator itr = flowTable.find(name);
792    if (itr != flowTable.end()) {
793        delete itr->second;
794        flowTable.erase(itr);
795    }
796}
797
798/**
799 * \brief Delete all flow object commands
800 *
801 * This will also delete the flow objects and hash table entries
802 */
803void
804NanoVis::deleteFlows(Tcl_Interp *interp)
805{
806    FlowHashmap::iterator itr;
807    for (itr = flowTable.begin();
808         itr != flowTable.end(); ++itr) {
809        Tcl_DeleteCommandFromToken(interp, itr->second->getCommandToken());
810    }
811    flowTable.clear();
812}
813
814bool
815NanoVis::mapFlows()
816{
817    TRACE("Enter");
818
819    /*
820     * Step 1. Get the overall min and max magnitudes of all the
821     *         flow vectors.
822     */
823    Flow::magMin = DBL_MAX;
824    Flow::magMax = -DBL_MAX;
825
826    for (FlowHashmap::iterator itr = flowTable.begin();
827         itr != flowTable.end(); ++itr) {
828        Flow *flow = itr->second;
829        double min, max;
830        if (!flow->isDataLoaded()) {
831            continue;
832        }
833        Rappture::Unirect3d *data = flow->data();
834        min = data->magMin();
835        max = data->magMax();
836        if (min < Flow::magMin) {
837            Flow::magMin = min;
838        } 
839        if (max > Flow::magMax) {
840            Flow::magMax = max;
841        }
842    }
843
844    TRACE("magMin=%g magMax=%g", Flow::magMin, Flow::magMax);
845
846    /*
847     * Step 2. Generate the vector field from each data set.
848     */
849    for (FlowHashmap::iterator itr = flowTable.begin();
850         itr != flowTable.end(); ++itr) {
851        Flow *flow = itr->second;
852        if (!flow->isDataLoaded()) {
853            continue; // Flow exists, but no data has been loaded yet.
854        }
855        if (flow->visible()) {
856            flow->initializeParticles();
857        }
858        if (!flow->scaleVectorField()) {
859            return false;
860        }
861    }
862
863    Flow::updatePending = false;
864    return true;
865}
866
867void
868NanoVis::getFlowBounds(Vector3f& min,
869                       Vector3f& max,
870                       bool onlyVisible)
871{
872    TRACE("Enter");
873
874    BBox allBounds;
875    for (FlowHashmap::iterator itr = flowTable.begin();
876         itr != flowTable.end(); ++itr) {
877        BBox bbox;
878        itr->second->getBounds(bbox.min, bbox.max, onlyVisible);
879        allBounds.extend(bbox);
880    }
881
882    min = allBounds.min;
883    max = allBounds.max;
884}
885
886void
887NanoVis::renderFlows()
888{
889    for (FlowHashmap::iterator itr = flowTable.begin();
890         itr != flowTable.end(); ++itr) {
891        Flow *flow = itr->second;
892        if (flow->isDataLoaded() && flow->visible()) {
893            flow->render();
894        }
895    }
896}
897
898void
899NanoVis::resetFlows()
900{
901    NanoVis::licRenderer->reset();
902    for (FlowHashmap::iterator itr = flowTable.begin();
903         itr != flowTable.end(); ++itr) {
904        Flow *flow = itr->second;
905        if (flow->isDataLoaded()) {
906            flow->resetParticles();
907        }
908    }
909}   
910
911void
912NanoVis::advectFlows()
913{
914    TRACE("Enter");
915    for (FlowHashmap::iterator itr = flowTable.begin();
916         itr != flowTable.end(); ++itr) {
917        Flow *flow = itr->second;
918        if (flow->isDataLoaded()) {
919            flow->advect();
920        }
921    }
922}
923
924void
925NanoVis::render()
926{
927    TRACE("Enter");
928
929    if (Flow::updatePending) {
930        mapFlows();
931    }
932    if (HeightMap::updatePending) {
933        setHeightmapRanges();
934    }
935    if (Volume::updatePending) {
936        setVolumeRanges();
937    }
938
939    collectBounds();
940
941    // Start final rendering
942
943    // Need to reset fbo since it may have been changed to default (0)
944    bindOffscreenBuffer();
945
946    //clear screen
947    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
948
949    glEnable(GL_DEPTH_TEST);
950    glEnable(GL_COLOR_MATERIAL);
951
952    // Emit modelview and projection matrices
953    _camera->initialize();
954
955    // Now render things in the scene
956
957    orientationIndicator->setPosition(sceneBounds.getCenter());
958    orientationIndicator->setScale(sceneBounds.getSize());
959    orientationIndicator->render();
960
961    grid->render();
962
963    licRenderer->render();
964
965    velocityArrowsSlice->render();
966
967    renderFlows();
968
969    volRenderer->renderAll();
970
971    for (HeightMapHashmap::iterator itr = heightMapTable.begin();
972         itr != heightMapTable.end(); ++itr) {
973        itr->second->render(renderContext);
974     }
975
976    CHECK_FRAMEBUFFER_STATUS();
977    TRACE("Leave");
978    redrawPending = false;
979}
Note: See TracBrowser for help on using the repository browser.