source: nanovis/trunk/nanovis.cpp @ 5048

Last change on this file since 5048 was 4949, checked in by ldelgass, 9 years ago

Bump version in configure, minor cleanups

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