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

Last change on this file since 5718 was 5718, checked in by ldelgass, 4 years ago

Merge from trunk

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