source: nanovis/trunk/nanovis.cpp @ 6307

Last change on this file since 6307 was 5722, checked in by ldelgass, 9 years ago

Set flags to update ranges when deleting objects

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