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

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

Merge r5478,r5480 from nanovis trunk (grid refactoring)

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