source: nanovis/branches/1.2/nanovis.cpp @ 5703

Last change on this file since 5703 was 5702, checked in by ldelgass, 9 years ago

Merge r5613 from nanovis trunk

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