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

Last change on this file since 4364 was 4167, checked in by ldelgass, 10 years ago

Texture environment is not part of texture object stored state, so don't set
environment mode before loading textures, should be done at render time.

  • Property svn:eol-style set to native
File size: 25.8 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    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
337    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
338#if defined(HAVE_FLOAT_TEXTURES) && defined(USE_HALF_FLOAT)
339    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, winWidth, winHeight, 0,
340                 GL_RGB, GL_INT, NULL);
341#elif defined(HAVE_FLOAT_TEXTURES)
342    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, winWidth, winHeight, 0,
343                 GL_RGB, GL_INT, NULL);
344#else
345    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, winWidth, winHeight, 0,
346                 GL_RGB, GL_INT, NULL);
347#endif
348
349    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _finalFbo);
350    glGenRenderbuffersEXT(1, &_finalDepthRb);
351    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
352    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24,
353                             winWidth, winHeight);
354    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
355                              GL_TEXTURE_2D, _finalColorTex, 0);
356    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
357                                 GL_RENDERBUFFER_EXT, _finalDepthRb);
358
359    GLenum status;
360    if (!CheckFBO(&status)) {
361        PrintFBOStatus(status, "finalFbo");
362        return false;
363    }
364
365    TRACE("Leave");
366    return true;
367}
368
369//resize the offscreen buffer
370bool
371NanoVis::resizeOffscreenBuffer(int w, int h)
372{
373    TRACE("Enter (%d, %d)", w, h);
374    if ((w == winWidth) && (h == winHeight)) {
375        return true;
376    }
377    winWidth = w;
378    winHeight = h;
379
380    if (fonts) {
381        fonts->resize(w, h);
382    }
383    TRACE("screenBuffer size: %d %d", w, h);
384
385    if (screenBuffer != NULL) {
386        delete [] screenBuffer;
387        screenBuffer = NULL;
388    }
389
390    screenBuffer = new unsigned char[3*winWidth*winHeight];
391    assert(screenBuffer != NULL);
392
393    //delete the current render buffer resources
394    glDeleteTextures(1, &_finalColorTex);
395    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
396    glDeleteRenderbuffersEXT(1, &_finalDepthRb);
397
398    TRACE("before deleteframebuffers");
399    glDeleteFramebuffersEXT(1, &_finalFbo);
400
401    TRACE("reinitialize FBO");
402    //Reinitialize final fbo for final display
403    glGenFramebuffersEXT(1, &_finalFbo);
404
405    glGenTextures(1, &_finalColorTex);
406    glBindTexture(GL_TEXTURE_2D, _finalColorTex);
407    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
408    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
409#if defined(HAVE_FLOAT_TEXTURES) && defined(USE_HALF_FLOAT)
410    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, winWidth, winHeight, 0,
411                 GL_RGB, GL_INT, NULL);
412#elif defined(HAVE_FLOAT_TEXTURES)
413    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, winWidth, winHeight, 0,
414                 GL_RGB, GL_INT, NULL);
415#else
416    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, winWidth, winHeight, 0,
417                 GL_RGB, GL_INT, NULL);
418#endif
419
420    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _finalFbo);
421    glGenRenderbuffersEXT(1, &_finalDepthRb);
422    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
423    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24,
424                             winWidth, winHeight);
425    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
426                              GL_TEXTURE_2D, _finalColorTex, 0);
427    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
428                                 GL_RENDERBUFFER_EXT, _finalDepthRb);
429
430    GLenum status;
431    if (!CheckFBO(&status)) {
432        PrintFBOStatus(status, "finalFbo");
433        return false;
434    }
435
436    TRACE("change camera");
437    //change the camera setting
438    _camera->setViewport(0, 0, winWidth, winHeight);
439    planeRenderer->setScreenSize(winWidth, winHeight);
440
441    TRACE("Leave (%d, %d)", w, h);
442    return true;
443}
444
445bool NanoVis::init(const char* path)
446{
447    // print OpenGL driver information
448    TRACE("-----------------------------------------------------------");
449    TRACE("OpenGL version: %s", glGetString(GL_VERSION));
450    TRACE("OpenGL vendor: %s", glGetString(GL_VENDOR));
451    TRACE("OpenGL renderer: %s", glGetString(GL_RENDERER));
452    TRACE("-----------------------------------------------------------");
453
454    if (path == NULL) {
455        ERROR("No path defined for shaders or resources");
456        return false;
457    }
458    GLenum err = glewInit();
459    if (GLEW_OK != err) {
460        ERROR("Can't init GLEW: %s", glewGetErrorString(err));
461        return false;
462    }
463    TRACE("Using GLEW %s", glewGetString(GLEW_VERSION));
464
465    // OpenGL 2.1 includes VBOs, PBOs, MRT, NPOT textures, point parameters, point sprites,
466    // GLSL 1.2, and occlusion queries.
467    if (!GLEW_VERSION_2_1) {
468        ERROR("OpenGL version 2.1 or greater is required");
469        return false;
470    }
471
472    // NVIDIA driver may report OpenGL 2.1, but not support PBOs in
473    // indirect GLX contexts
474    if (!GLEW_ARB_pixel_buffer_object) {
475        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).");
476        return false;
477    }
478
479    // Additional extensions not in 2.1 core
480
481    // Framebuffer objects were promoted in 3.0
482    if (!GLEW_EXT_framebuffer_object) {
483        ERROR("EXT_framebuffer_oject extension is required");
484        return false;
485    }
486    // Rectangle textures were promoted in 3.1
487    // FIXME: can use NPOT in place of rectangle textures
488    if (!GLEW_ARB_texture_rectangle) {
489        ERROR("ARB_texture_rectangle extension is required");
490        return false;
491    }
492#ifdef HAVE_FLOAT_TEXTURES
493    // Float color buffers and textures were promoted in 3.0
494    if (!GLEW_ARB_texture_float ||
495        !GLEW_ARB_color_buffer_float) {
496        ERROR("ARB_texture_float and ARB_color_buffer_float extensions are required");
497        return false;
498    }
499#endif
500    // FIXME: should use GLSL for portability
501#ifdef USE_ARB_PROGRAMS
502    if (!GLEW_ARB_vertex_program ||
503        !GLEW_ARB_fragment_program) {
504        ERROR("ARB_vertex_program and ARB_fragment_program extensions are required");
505        return false;
506    }
507#else
508    if (!GLEW_NV_vertex_program3 ||
509        !GLEW_NV_fragment_program2) {
510        ERROR("NV_vertex_program3 and NV_fragment_program2 extensions are required");
511        return false;
512    }
513#endif
514
515    if (!FilePath::getInstance()->setPath(path)) {
516        ERROR("can't set file path to %s", path);
517        return false;
518    }
519
520    ImageLoaderFactory::getInstance()->addLoaderImpl("bmp", new BMPImageLoaderImpl());
521
522    return true;
523}
524
525bool
526NanoVis::initGL()
527{
528    TRACE("in initGL");
529    //buffer to store data read from the screen
530    if (screenBuffer) {
531        delete[] screenBuffer;
532        screenBuffer = NULL;
533    }
534    screenBuffer = new unsigned char[3*winWidth*winHeight];
535    assert(screenBuffer != NULL);
536
537    //create the camera with default setting
538    _camera = new Camera(0, 0, winWidth, winHeight);
539
540    glEnable(GL_TEXTURE_2D);
541    glShadeModel(GL_FLAT);
542    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
543    glClearColor(0, 0, 0, 1);
544    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
545
546    //initialize lighting
547    GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
548    GLfloat mat_shininess[] = {30.0};
549    GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};
550
551    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
552    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
553    glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
554    glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
555    glLightfv(GL_LIGHT1, GL_DIFFUSE, white_light);
556    glLightfv(GL_LIGHT1, GL_SPECULAR, white_light);
557
558    //frame buffer object for offscreen rendering
559    if (!initOffscreenBuffer()) {
560        return false;
561    }
562
563    fonts = new Fonts();
564    fonts->addFont("verdana", "verdana.fnt");
565    fonts->setFont("verdana");
566
567    renderContext = new RenderContext();
568
569    volRenderer = new VolumeRenderer();
570
571    planeRenderer = new PlaneRenderer(winWidth, winHeight);
572
573    velocityArrowsSlice = new VelocityArrowsSlice();
574
575    licRenderer = new LIC();
576
577    orientationIndicator = new OrientationIndicator();
578
579    grid = new Grid();
580    grid->setFont(fonts);
581
582    //assert(glGetError()==0);
583
584    TRACE("leaving initGL");
585    return true;
586}
587
588void NanoVis::update()
589{
590    VolumeInterpolator *volInterp = volRenderer->getVolumeInterpolator();
591    if (volInterp->isStarted()) {
592        struct timeval clock;
593        gettimeofday(&clock, NULL);
594        double elapsed_time;
595
596        elapsed_time = clock.tv_sec + clock.tv_usec/1000000.0 -
597            volInterp->getStartTime();
598
599        TRACE("%lf %lf", elapsed_time,
600              volInterp->getInterval());
601        float fraction;
602        float f;
603
604        f = fmod((float) elapsed_time, (float)volInterp->getInterval());
605        if (f == 0.0) {
606            fraction = 0.0f;
607        } else {
608            fraction = f / volInterp->getInterval();
609        }
610        TRACE("fraction : %f", fraction);
611        volInterp->update(fraction);
612    }
613}
614
615/**
616 * \brief Called when new volumes are added to update ranges
617 */
618void
619NanoVis::setVolumeRanges()
620{
621    TRACE("Enter");
622
623    double valueMin = DBL_MAX, valueMax = -DBL_MAX;
624    VolumeHashmap::iterator itr;
625    for (itr = volumeTable.begin();
626         itr != volumeTable.end(); ++itr) {
627        Volume *volume = itr->second;
628        if (valueMin > volume->wAxis.min()) {
629            valueMin = volume->wAxis.min();
630        }
631        if (valueMax < volume->wAxis.max()) {
632            valueMax = volume->wAxis.max();
633        }
634    }
635    if ((valueMin < DBL_MAX) && (valueMax > -DBL_MAX)) {
636        Volume::valueMin = valueMin;
637        Volume::valueMax = valueMax;
638    }
639    Volume::updatePending = false;
640    TRACE("Leave");
641}
642
643/**
644 * \brief Called when new heightmaps are added to update ranges
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    sceneBounds.makeEmpty();
679
680    for (VolumeHashmap::iterator itr = volumeTable.begin();
681         itr != volumeTable.end(); ++itr) {
682        Volume *volume = itr->second;
683
684        if (onlyVisible && !volume->visible())
685            continue;
686
687        BBox bbox;
688        volume->getBounds(bbox.min, bbox.max);
689        sceneBounds.extend(bbox);
690    }
691
692    for (HeightMapHashmap::iterator itr = heightMapTable.begin();
693         itr != heightMapTable.end(); ++itr) {
694        HeightMap *heightMap = itr->second;
695
696        if (onlyVisible && !heightMap->isVisible())
697            continue;
698
699        BBox bbox;
700        heightMap->getBounds(bbox.min, bbox.max);
701        sceneBounds.extend(bbox);
702    }
703
704    for (FlowHashmap::iterator itr = flowTable.begin();
705         itr != flowTable.end(); ++itr) {
706        Flow *flow = itr->second;
707
708        BBox bbox;
709        flow->getBounds(bbox.min, bbox.max, onlyVisible);
710        sceneBounds.extend(bbox);
711    }
712
713    if (!sceneBounds.isEmptyX()) {
714        grid->xAxis.setScale(sceneBounds.min.x, sceneBounds.max.x);
715    }
716    if (!sceneBounds.isEmptyY()) {
717        grid->yAxis.setScale(sceneBounds.min.y, sceneBounds.max.y);
718    }
719    if (!sceneBounds.isEmptyZ()) {
720        grid->zAxis.setScale(sceneBounds.min.z, sceneBounds.max.z);
721    }
722
723#if 0
724    if (!onlyVisible || grid->isVisible()) {
725        BBox bbox;
726        grid->getBounds(bbox.min, bbox.max);
727        sceneBounds.extend(bbox);
728    }
729#endif
730
731    if (sceneBounds.isEmpty()) {
732        sceneBounds.min.set(-0.5, -0.5, -0.5);
733        sceneBounds.max.set( 0.5,  0.5,  0.5);
734    }
735
736    TRACE("Scene bounds: (%g,%g,%g) - (%g,%g,%g)",
737          sceneBounds.min.x, sceneBounds.min.y, sceneBounds.min.z,
738          sceneBounds.max.x, sceneBounds.max.y, sceneBounds.max.z);
739}
740
741void
742NanoVis::setBgColor(float color[3])
743{
744    TRACE("Setting bgcolor to %g %g %g", color[0], color[1], color[2]);
745    glClearColor(color[0], color[1], color[2], 1);
746}
747
748void
749NanoVis::removeVolume(Volume *volume)
750{
751    VolumeHashmap::iterator itr = volumeTable.find(volume->name());
752    if (itr != volumeTable.end()) {
753        volumeTable.erase(itr);
754    }
755    delete volume;
756}
757
758Flow *
759NanoVis::getFlow(const char *name)
760{
761    FlowHashmap::iterator itr = flowTable.find(name);
762    if (itr == flowTable.end()) {
763        TRACE("Can't find flow '%s'", name);
764        return NULL;
765    }
766    return itr->second;
767}
768
769Flow *
770NanoVis::createFlow(Tcl_Interp *interp, const char *name)
771{
772    FlowHashmap::iterator itr = flowTable.find(name);
773    if (itr != flowTable.end()) {
774        ERROR("Flow '%s' already exists", name);
775        return NULL;
776    }
777    Flow *flow = new Flow(interp, name);
778    flowTable[name] = flow;
779    return flow;
780}
781
782/**
783 * \brief Delete flow object and hash table entry
784 *
785 * This is called by the flow command instance delete callback
786 */
787void
788NanoVis::deleteFlow(const char *name)
789{
790    FlowHashmap::iterator itr = flowTable.find(name);
791    if (itr != flowTable.end()) {
792        delete itr->second;
793        flowTable.erase(itr);
794    }
795}
796
797/**
798 * \brief Delete all flow object commands
799 *
800 * This will also delete the flow objects and hash table entries
801 */
802void
803NanoVis::deleteFlows(Tcl_Interp *interp)
804{
805    FlowHashmap::iterator itr;
806    for (itr = flowTable.begin();
807         itr != flowTable.end(); ++itr) {
808        Tcl_DeleteCommandFromToken(interp, itr->second->getCommandToken());
809    }
810    flowTable.clear();
811}
812
813/**
814 * \brief Called when new flows are added to update ranges
815 */
816bool
817NanoVis::setFlowRanges()
818{
819    TRACE("Enter");
820
821    /*
822     * Step 1. Get the overall min and max magnitudes of all the
823     *         flow vectors.
824     */
825    Flow::magMin = DBL_MAX;
826    Flow::magMax = -DBL_MAX;
827
828    for (FlowHashmap::iterator itr = flowTable.begin();
829         itr != flowTable.end(); ++itr) {
830        Flow *flow = itr->second;
831        if (!flow->isDataLoaded()) {
832            continue;
833        }
834        double range[2];
835        flow->getVectorRange(range);
836        if (range[0] < Flow::magMin) {
837            Flow::magMin = range[0];
838        }
839        if (range[1] > Flow::magMax) {
840            Flow::magMax = range[1];
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    }
859
860    Flow::updatePending = false;
861    return true;
862}
863
864void
865NanoVis::renderFlows()
866{
867    for (FlowHashmap::iterator itr = flowTable.begin();
868         itr != flowTable.end(); ++itr) {
869        Flow *flow = itr->second;
870        if (flow->isDataLoaded() && flow->visible()) {
871            flow->render();
872        }
873    }
874}
875
876void
877NanoVis::resetFlows()
878{
879    NanoVis::licRenderer->reset();
880    for (FlowHashmap::iterator itr = flowTable.begin();
881         itr != flowTable.end(); ++itr) {
882        Flow *flow = itr->second;
883        if (flow->isDataLoaded()) {
884            flow->resetParticles();
885        }
886    }
887}   
888
889void
890NanoVis::advectFlows()
891{
892    TRACE("Enter");
893    for (FlowHashmap::iterator itr = flowTable.begin();
894         itr != flowTable.end(); ++itr) {
895        Flow *flow = itr->second;
896        if (flow->isDataLoaded()) {
897            flow->advect();
898        }
899    }
900}
901
902void
903NanoVis::render()
904{
905    TRACE("Enter");
906
907    if (Flow::updatePending) {
908        setFlowRanges();
909    }
910    if (HeightMap::updatePending) {
911        setHeightmapRanges();
912    }
913    if (Volume::updatePending) {
914        setVolumeRanges();
915    }
916
917    collectBounds();
918
919    // Start final rendering
920
921    // Need to reset fbo since it may have been changed to default (0)
922    bindOffscreenBuffer();
923
924    //clear screen
925    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
926
927    glEnable(GL_DEPTH_TEST);
928    glEnable(GL_COLOR_MATERIAL);
929
930    // Emit modelview and projection matrices
931    _camera->initialize();
932
933    // Now render things in the scene
934
935    orientationIndicator->setPosition(sceneBounds.getCenter());
936    orientationIndicator->setScale(sceneBounds.getSize());
937    orientationIndicator->render();
938
939    grid->render();
940
941    licRenderer->render();
942
943    velocityArrowsSlice->render();
944
945    renderFlows();
946
947    volRenderer->renderAll();
948
949    for (HeightMapHashmap::iterator itr = heightMapTable.begin();
950         itr != heightMapTable.end(); ++itr) {
951        itr->second->render(renderContext);
952     }
953
954    CHECK_FRAMEBUFFER_STATUS();
955    TRACE("Leave");
956    redrawPending = false;
957}
Note: See TracBrowser for help on using the repository browser.