source: nanovis/branches/1.1/nanovis.cpp @ 4882

Last change on this file since 4882 was 4879, checked in by ldelgass, 9 years ago

Fix for ExecuteCommand?

  • Property svn:eol-style set to native
File size: 41.0 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 <sys/types.h>
20#include <sys/uio.h> // for writev in ppmWrite
21#include <unistd.h>
22
23#include <cassert>
24#include <cstdlib>
25#include <cstdio>
26#include <cstring>
27#include <cmath>
28
29#include <string>
30
31#include <GL/glew.h>
32#include <GL/glut.h>
33
34#include <graphics/RenderContext.h>
35#include <vrmath/Vector3f.h>
36
37#include <util/FilePath.h>
38#include <util/Fonts.h>
39
40#include <BMPImageLoaderImpl.h>
41#include <ImageLoaderFactory.h>
42
43#include "config.h"
44#include "nanovis.h"
45#include "nanovisServer.h"
46#include "define.h"
47
48#include "Flow.h"
49#include "Grid.h"
50#include "HeightMap.h"
51#include "NvCamera.h"
52#include "NvLIC.h"
53#include "NvShader.h"
54#include "PlaneRenderer.h"
55#include "Texture2D.h"
56#include "Trace.h"
57#include "TransferFunction.h"
58#include "Unirect.h"
59#include "VelocityArrowsSlice.h"
60#include "Volume.h"
61#include "VolumeInterpolator.h"
62#include "VolumeRenderer.h"
63
64using namespace nv;
65using namespace nv::graphics;
66using namespace nv::util;
67using namespace vrmath;
68
69#define SIZEOF_BMP_HEADER   54
70
71// STATIC MEMBER DATA
72
73Tcl_Interp *NanoVis::interp = NULL;
74
75unsigned int NanoVis::flags = 0;
76bool NanoVis::debugFlag = false;
77bool NanoVis::axisOn = true;
78
79int NanoVis::winWidth = NPIX;
80int NanoVis::winHeight = NPIX;
81int NanoVis::renderWindow = 0;
82unsigned char *NanoVis::screenBuffer = NULL;
83Texture2D *NanoVis::legendTexture = NULL;
84Fonts *NanoVis::fonts;
85int NanoVis::updir = Y_POS;
86NvCamera *NanoVis::cam = NULL;
87RenderContext *NanoVis::renderContext = NULL;
88
89NanoVis::TransferFunctionHashmap NanoVis::tfTable;
90NanoVis::VolumeHashmap NanoVis::volumeTable;
91NanoVis::FlowHashmap NanoVis::flowTable;
92NanoVis::HeightMapHashmap NanoVis::heightMapTable;
93
94double NanoVis::magMin = DBL_MAX;
95double NanoVis::magMax = -DBL_MAX;
96float NanoVis::xMin = FLT_MAX;
97float NanoVis::xMax = -FLT_MAX;
98float NanoVis::yMin = FLT_MAX;
99float NanoVis::yMax = -FLT_MAX;
100float NanoVis::zMin = FLT_MAX;
101float NanoVis::zMax = -FLT_MAX;
102float NanoVis::wMin = FLT_MAX;
103float NanoVis::wMax = -FLT_MAX;
104Vector3f NanoVis::sceneMin, NanoVis::sceneMax;
105
106VolumeRenderer *NanoVis::volRenderer = NULL;
107VelocityArrowsSlice *NanoVis::velocityArrowsSlice = NULL;
108NvLIC *NanoVis::licRenderer = NULL;
109PlaneRenderer *NanoVis::planeRenderer = NULL;
110Grid *NanoVis::grid = NULL;
111
112// Image based flow visualization slice location
113// FLOW
114float NanoVis::_licSlice = 0.5f;
115int NanoVis::_licAxis = 2; // z axis
116
117//frame buffer for final rendering
118GLuint NanoVis::_finalFbo = 0;
119GLuint NanoVis::_finalColorTex = 0;
120GLuint NanoVis::_finalDepthRb = 0;
121
122// Default camera location.
123float def_eye_x = 0.0f;
124float def_eye_y = 0.0f;
125float def_eye_z = 2.5f;
126
127void
128NanoVis::removeAllData()
129{
130    TRACE("Enter");
131    if (grid != NULL) {
132        TRACE("Deleting grid");
133        delete grid;
134    }
135    if (cam != NULL) {
136        TRACE("Deleting cam");
137        delete cam;
138    }
139    if (volRenderer != NULL) {
140        TRACE("Deleting volRenderer");
141        delete volRenderer;
142    }
143    if (planeRenderer != NULL) {
144        TRACE("Deleting planeRenderer");
145        delete planeRenderer;
146    }
147    if (legendTexture != NULL) {
148        TRACE("Deleting legendTexture");
149        delete legendTexture;
150    }
151    TRACE("Deleting flows");
152    deleteFlows(interp);
153    if (licRenderer != NULL) {
154        TRACE("Deleting licRenderer");
155        delete licRenderer;
156    }
157    if (velocityArrowsSlice != NULL) {
158        TRACE("Deleting velocityArrowsSlice");
159        delete velocityArrowsSlice;
160    }
161    if (renderContext != NULL) {
162        TRACE("Deleting renderContext");
163        delete renderContext;
164    }
165    if (screenBuffer != NULL) {
166        TRACE("Deleting screenBuffer");
167        delete [] screenBuffer;
168        screenBuffer = NULL;
169    }
170    if (fonts != NULL) {
171        TRACE("Deleting fonts");
172        delete fonts;
173    }
174    TRACE("Leave");
175}
176
177void
178NanoVis::eventuallyRedraw(unsigned int flag)
179{
180    if (flag) {
181        flags |= flag;
182    }
183    if ((flags & REDRAW_PENDING) == 0) {
184        glutPostRedisplay();
185        flags |= REDRAW_PENDING;
186    }
187}
188
189void
190NanoVis::pan(float dx, float dy)
191{
192    /* Move the camera and its target by equal amounts along the x and y
193     * axes. */
194    TRACE("pan: x=%f, y=%f", dx, dy);
195
196    cam->x(def_eye_x - dx);
197    cam->y(def_eye_y + dy);
198    TRACE("set eye to %f %f", cam->x(), cam->y());
199}
200
201void
202NanoVis::zoom(float z)
203{
204    /* Move the camera and its target by equal amounts along the x and y
205     * axes. */
206    TRACE("zoom: z=%f", z);
207
208    cam->z(def_eye_z / z);
209
210    collectBounds();
211    cam->resetClippingRange(sceneMin, sceneMax);
212
213    TRACE("set cam z to %f", cam->z());
214}
215
216void
217NanoVis::resetCamera(bool resetOrientation)
218{
219    TRACE("Resetting all=%d", resetOrientation ? 1 : 0);
220
221    collectBounds();
222    cam->reset(sceneMin, sceneMax, resetOrientation);
223
224    def_eye_x = cam->x();
225    def_eye_y = cam->y();
226    def_eye_z = cam->z();
227}
228
229/** \brief Load a 3D volume
230 *
231 * \param name Volume ID
232 * \param width Number of samples in X direction
233 * \param height Number of samples in Y direction
234 * \param depth Number of samples in Z direction
235 * \param numComponents the number of scalars for each space point. All component
236 * scalars for a point are placed consequtively in data array
237 * width, height and depth: number of points in each dimension
238 * \param data Array of floats
239 * \param vmin Min value of field
240 * \param vmax Max value of field
241 * \param nonZeroMin Minimum non-zero value of field
242 * \param data pointer to an array of floats.
243 */
244Volume *
245NanoVis::loadVolume(const char *name, int width, int height, int depth,
246                    int numComponents, float *data, double vmin, double vmax,
247                    double nonZeroMin)
248{
249    VolumeHashmap::iterator itr = volumeTable.find(name);
250    if (itr != volumeTable.end()) {
251        WARN("volume \"%s\" already exists", name);
252        removeVolume(itr->second);
253    }
254
255    Volume *volume = new Volume(0.f, 0.f, 0.f,
256                                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        ppmWrite(prefix);
329    }
330    planeRenderer->removePlane(index);
331    resizeOffscreenBuffer(old_width, old_height);
332
333    delete legendTexture;
334    legendTexture = NULL;
335    TRACE("Leave");
336    return TCL_OK;
337}
338
339//initialize frame buffer objects for offscreen rendering
340bool
341NanoVis::initOffscreenBuffer()
342{
343    TRACE("Enter");
344    assert(_finalFbo == 0);
345    // Initialize a fbo for final display.
346    glGenFramebuffersEXT(1, &_finalFbo);
347
348    glGenTextures(1, &_finalColorTex);
349    glBindTexture(GL_TEXTURE_2D, _finalColorTex);
350
351    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
352    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
353    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
354
355#if defined(HAVE_FLOAT_TEXTURES) && defined(USE_HALF_FLOAT)
356    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, winWidth, winHeight, 0,
357                 GL_RGB, GL_INT, NULL);
358#elif defined(HAVE_FLOAT_TEXTURES)
359    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, winWidth, winHeight, 0,
360                 GL_RGB, GL_INT, NULL);
361#else
362    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, winWidth, winHeight, 0,
363                 GL_RGB, GL_INT, NULL);
364#endif
365
366    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _finalFbo);
367    glGenRenderbuffersEXT(1, &_finalDepthRb);
368    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
369    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24,
370                             winWidth, winHeight);
371    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
372                              GL_TEXTURE_2D, _finalColorTex, 0);
373    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
374                                 GL_RENDERBUFFER_EXT, _finalDepthRb);
375
376    GLenum status;
377    if (!CheckFBO(&status)) {
378        PrintFBOStatus(status, "finalFbo");
379        return false;
380    }
381
382    TRACE("Leave");
383    return true;
384}
385
386//resize the offscreen buffer
387bool
388NanoVis::resizeOffscreenBuffer(int w, int h)
389{
390    TRACE("Enter (%d, %d)", w, h);
391    if ((w == winWidth) && (h == winHeight)) {
392        return true;
393    }
394    winWidth = w;
395    winHeight = h;
396
397    if (fonts) {
398        fonts->resize(w, h);
399    }
400    TRACE("screenBuffer size: %d %d", w, h);
401
402    if (screenBuffer != NULL) {
403        delete [] screenBuffer;
404        screenBuffer = NULL;
405    }
406
407    screenBuffer = new unsigned char[4*winWidth*winHeight];
408    assert(screenBuffer != NULL);
409
410    //delete the current render buffer resources
411    glDeleteTextures(1, &_finalColorTex);
412    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
413    glDeleteRenderbuffersEXT(1, &_finalDepthRb);
414
415    TRACE("before deleteframebuffers");
416    glDeleteFramebuffersEXT(1, &_finalFbo);
417
418    TRACE("reinitialize FBO");
419    //Reinitialize final fbo for final display
420    glGenFramebuffersEXT(1, &_finalFbo);
421
422    glGenTextures(1, &_finalColorTex);
423    glBindTexture(GL_TEXTURE_2D, _finalColorTex);
424
425    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
426    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
427    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
428
429#if defined(HAVE_FLOAT_TEXTURES) && defined(USE_HALF_FLOAT)
430    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, winWidth, winHeight, 0,
431                 GL_RGB, GL_INT, NULL);
432#elif defined(HAVE_FLOAT_TEXTURES)
433    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, winWidth, winHeight, 0,
434                 GL_RGB, GL_INT, NULL);
435#else
436    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, winWidth, winHeight, 0,
437                 GL_RGB, GL_INT, NULL);
438#endif
439
440    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _finalFbo);
441    glGenRenderbuffersEXT(1, &_finalDepthRb);
442    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
443    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24,
444                             winWidth, winHeight);
445    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
446                              GL_TEXTURE_2D, _finalColorTex, 0);
447    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
448                                 GL_RENDERBUFFER_EXT, _finalDepthRb);
449
450    GLenum status;
451    if (!CheckFBO(&status)) {
452        PrintFBOStatus(status, "finalFbo");
453        return false;
454    }
455
456    TRACE("change camera");
457    //change the camera setting
458    cam->setScreenSize(0, 0, winWidth, winHeight);
459    planeRenderer->setScreenSize(winWidth, winHeight);
460
461    TRACE("Leave (%d, %d)", w, h);
462    return true;
463}
464
465static
466void cgErrorCallback(void)
467{
468    if (!NvShader::printErrorInfo()) {
469        TRACE("Cg error, exiting...");
470        exit(1);
471    }
472}
473
474bool NanoVis::init(const char* path)
475{
476    // print OpenGL driver information
477    TRACE("-----------------------------------------------------------");
478    TRACE("OpenGL version: %s", glGetString(GL_VERSION));
479    TRACE("OpenGL vendor: %s", glGetString(GL_VENDOR));
480    TRACE("OpenGL renderer: %s", glGetString(GL_RENDERER));
481    TRACE("-----------------------------------------------------------");
482
483    if (path == NULL) {
484        ERROR("No path defined for shaders or resources");
485        return false;
486    }
487    GLenum err = glewInit();
488    if (GLEW_OK != err) {
489        ERROR("Can't init GLEW: %s", glewGetErrorString(err));
490        return false;
491    }
492    TRACE("Using GLEW %s", glewGetString(GLEW_VERSION));
493
494    // OpenGL 2.1 includes VBOs, PBOs, MRT, NPOT textures, point parameters, point sprites,
495    // GLSL 1.2, and occlusion queries.
496    if (!GLEW_VERSION_2_1) {
497        ERROR("OpenGL version 2.1 or greater is required");
498        return false;
499    }
500
501    // NVIDIA driver may report OpenGL 2.1, but not support PBOs in
502    // indirect GLX contexts
503    if (!GLEW_ARB_pixel_buffer_object) {
504        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).");
505        return false;
506    }
507
508    // Additional extensions not in 2.1 core
509
510    // Framebuffer objects were promoted in 3.0
511    if (!GLEW_EXT_framebuffer_object) {
512        ERROR("EXT_framebuffer_oject extension is required");
513        return false;
514    }
515    // Rectangle textures were promoted in 3.1
516    // FIXME: can use NPOT in place of rectangle textures
517    if (!GLEW_ARB_texture_rectangle) {
518        ERROR("ARB_texture_rectangle extension is required");
519        return false;
520    }
521#ifdef HAVE_FLOAT_TEXTURES
522    // Float color buffers and textures were promoted in 3.0
523    if (!GLEW_ARB_texture_float ||
524        !GLEW_ARB_color_buffer_float) {
525        ERROR("ARB_texture_float and ARB_color_buffer_float extensions are required");
526        return false;
527    }
528#endif
529    // FIXME: should use ARB programs or (preferably) a GLSL profile for portability
530    if (!GLEW_NV_vertex_program3 ||
531        !GLEW_NV_fragment_program2) {
532        ERROR("NV_vertex_program3 and NV_fragment_program2 extensions are required");
533        return false;
534    }
535
536    if (!FilePath::getInstance()->setPath(path)) {
537        ERROR("can't set file path to %s", path);
538        return false;
539    }
540
541    ImageLoaderFactory::getInstance()->addLoaderImpl("bmp", new BMPImageLoaderImpl());
542
543    NvShader::initCg();
544    NvShader::setErrorCallback(cgErrorCallback);
545
546    fonts = new Fonts();
547    fonts->addFont("verdana", "verdana.fnt");
548    fonts->setFont("verdana");
549
550    velocityArrowsSlice = new VelocityArrowsSlice;
551    licRenderer = new NvLIC(NMESH, NPIX, NPIX, _licAxis, _licSlice);
552
553    grid = new Grid();
554    grid->setFont(fonts);
555
556    return true;
557}
558
559bool
560NanoVis::initGL()
561{
562    TRACE("in initGL");
563    //buffer to store data read from the screen
564    if (screenBuffer) {
565        delete[] screenBuffer;
566        screenBuffer = NULL;
567    }
568    screenBuffer = new unsigned char[4*winWidth*winHeight];
569    assert(screenBuffer != NULL);
570
571    //create the camera with default setting
572    cam = new NvCamera(0, 0, winWidth, winHeight,
573                       def_eye_x, def_eye_y, def_eye_z);
574
575    glEnable(GL_TEXTURE_2D);
576    glShadeModel(GL_FLAT);
577    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
578    glClearColor(0, 0, 0, 1);
579    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
580
581    //initialize lighting
582    GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
583    GLfloat mat_shininess[] = {30.0};
584    GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};
585    GLfloat green_light[] = {0.1, 0.5, 0.1, 1.0};
586
587    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
588    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
589    glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
590    glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
591    glLightfv(GL_LIGHT1, GL_DIFFUSE, green_light);
592    glLightfv(GL_LIGHT1, GL_SPECULAR, white_light);
593
594    //frame buffer object for offscreen rendering
595    if (!initOffscreenBuffer()) {
596        return false;
597    }
598
599    volRenderer = new VolumeRenderer();
600
601    renderContext = new RenderContext();
602
603    planeRenderer = new PlaneRenderer(winWidth, winHeight);
604
605    //assert(glGetError()==0);
606
607    TRACE("leaving initGL");
608
609    return true;
610}
611
612// used internally to build up the BMP file header
613// Writes an integer value into the header data structure at pos
614static inline void
615bmpHeaderAddInt(unsigned char* header, int& pos, int data)
616{
617#ifdef WORDS_BIGENDIAN
618    header[pos++] = (data >> 24) & 0xFF;
619    header[pos++] = (data >> 16) & 0xFF;
620    header[pos++] = (data >> 8)  & 0xFF;
621    header[pos++] = (data)       & 0xFF;
622#else
623    header[pos++] = data & 0xff;
624    header[pos++] = (data >> 8) & 0xff;
625    header[pos++] = (data >> 16) & 0xff;
626    header[pos++] = (data >> 24) & 0xff;
627#endif
628}
629
630void
631NanoVis::bmpWriteToFile(int frame_number, const char *directory_name)
632{
633    unsigned char header[SIZEOF_BMP_HEADER];
634    int pos = 0;
635    header[pos++] = 'B';
636    header[pos++] = 'M';
637
638    // BE CAREFUL:  BMP files must have an even multiple of 4 bytes
639    // on each scan line.  If need be, we add padding to each line.
640    int pad = 0;
641    if ((3*winWidth) % 4 > 0) {
642        pad = 4 - ((3*winWidth) % 4);
643    }
644
645    // file size in bytes
646    int fsize = (3*winWidth+pad)*winHeight + SIZEOF_BMP_HEADER;
647    bmpHeaderAddInt(header, pos, fsize);
648
649    // reserved value (must be 0)
650    bmpHeaderAddInt(header, pos, 0);
651
652    // offset in bytes to start of bitmap data
653    bmpHeaderAddInt(header, pos, SIZEOF_BMP_HEADER);
654
655    // size of the BITMAPINFOHEADER
656    bmpHeaderAddInt(header, pos, 40);
657
658    // width of the image in pixels
659    bmpHeaderAddInt(header, pos, winWidth);
660
661    // height of the image in pixels
662    bmpHeaderAddInt(header, pos, winHeight);
663
664    // 1 plane + (24 bits/pixel << 16)
665    bmpHeaderAddInt(header, pos, 1572865);
666
667    // no compression
668    // size of image for compression
669    bmpHeaderAddInt(header, pos, 0);
670    bmpHeaderAddInt(header, pos, 0);
671
672    // x pixels per meter
673    // y pixels per meter
674    bmpHeaderAddInt(header, pos, 0);
675    bmpHeaderAddInt(header, pos, 0);
676
677    // number of colors used (0 = compute from bits/pixel)
678    // number of important colors (0 = all colors important)
679    bmpHeaderAddInt(header, pos, 0);
680    bmpHeaderAddInt(header, pos, 0);
681
682    // BE CAREFUL: BMP format wants BGR ordering for screen data
683    unsigned char* scr = screenBuffer;
684    for (int row=0; row < winHeight; row++) {
685        for (int col=0; col < winWidth; col++) {
686            unsigned char tmp = scr[2];
687            scr[2] = scr[0];  // B
688            scr[0] = tmp;     // R
689            scr += 3;
690        }
691        scr += pad;  // skip over padding already in screen data
692    }
693
694    FILE* f;
695    char filename[100];
696    if (frame_number >= 0) {
697        if (directory_name)
698            sprintf(filename, "%s/image%03d.bmp", directory_name, frame_number);
699        else
700            sprintf(filename, "/tmp/flow_animation/image%03d.bmp", frame_number);
701
702        TRACE("Writing %s", filename);
703        f = fopen(filename, "wb");
704        if (f == 0) {
705            ERROR("cannot create file");
706        }
707    } else {
708        f = fopen("/tmp/image.bmp", "wb");
709        if (f == 0) {
710            ERROR("cannot create file");
711        }
712    }
713    if (fwrite(header, SIZEOF_BMP_HEADER, 1, f) != 1) {
714        ERROR("can't write header: short write");
715    }
716    if (fwrite(screenBuffer, (3*winWidth+pad)*winHeight, 1, f) != 1) {
717        ERROR("can't write data: short write");
718    }
719    fclose(f);
720}
721
722void
723NanoVis::bmpWrite(const char *prefix)
724{
725    unsigned char header[SIZEOF_BMP_HEADER];
726    ssize_t nWritten;
727    int pos = 0;
728
729    // BE CAREFUL:  BMP files must have an even multiple of 4 bytes
730    // on each scan line.  If need be, we add padding to each line.
731    int pad = 0;
732    if ((3*winWidth) % 4 > 0) {
733        pad = 4 - ((3*winWidth) % 4);
734    }
735    pad = 0;
736    int fsize = (3*winWidth+pad)*winHeight + sizeof(header);
737
738    char string[200];
739    sprintf(string, "%s %d\n", prefix, fsize);
740    nWritten = write(g_fdOut, string, strlen(string));
741    assert(nWritten == (ssize_t)strlen(string));
742    header[pos++] = 'B';
743    header[pos++] = 'M';
744
745    // file size in bytes
746    bmpHeaderAddInt(header, pos, fsize);
747
748    // reserved value (must be 0)
749    bmpHeaderAddInt(header, pos, 0);
750
751    // offset in bytes to start of bitmap data
752    bmpHeaderAddInt(header, pos, SIZEOF_BMP_HEADER);
753
754    // size of the BITMAPINFOHEADER
755    bmpHeaderAddInt(header, pos, 40);
756
757    // width of the image in pixels
758    bmpHeaderAddInt(header, pos, winWidth);
759
760    // height of the image in pixels
761    bmpHeaderAddInt(header, pos, winHeight);
762
763    // 1 plane + (24 bits/pixel << 16)
764    bmpHeaderAddInt(header, pos, 1572865);
765
766    // no compression
767    // size of image for compression
768    bmpHeaderAddInt(header, pos, 0);
769    bmpHeaderAddInt(header, pos, 0);
770
771    // x pixels per meter
772    // y pixels per meter
773    bmpHeaderAddInt(header, pos, 0);
774    bmpHeaderAddInt(header, pos, 0);
775
776    // number of colors used (0 = compute from bits/pixel)
777    // number of important colors (0 = all colors important)
778    bmpHeaderAddInt(header, pos, 0);
779    bmpHeaderAddInt(header, pos, 0);
780
781    // BE CAREFUL: BMP format wants BGR ordering for screen data
782    unsigned char* scr = screenBuffer;
783    for (int row=0; row < winHeight; row++) {
784        for (int col=0; col < winWidth; col++) {
785            unsigned char tmp = scr[2];
786            scr[2] = scr[0];  // B
787            scr[0] = tmp;     // R
788            scr += 3;
789        }
790        scr += pad;  // skip over padding already in screen data
791    }
792
793    nWritten = write(g_fdOut, header, SIZEOF_BMP_HEADER);
794    assert(nWritten == SIZEOF_BMP_HEADER);
795    nWritten = write(g_fdOut, screenBuffer, (3*winWidth+pad)*winHeight);
796    assert(nWritten == (3*winWidth+pad)*winHeight);
797    g_stats.nFrames++;
798    g_stats.nFrameBytes += (3*winWidth+pad)*winHeight;
799}
800
801/*
802 * ppmWrite --
803 *
804 *  Writes the screen image as PPM binary data to the nanovisviewer
805 *  client.  The PPM binary format is very simple.
806 *
807 *      P6 w h 255\n
808 *      3-byte RGB pixel data.
809 *
810 *  The nanovisviewer client (using the TkImg library) will do less work
811 *  to unpack this format, as opposed to BMP or PNG.  (This doesn't
812 *  eliminate the need to look into DXT compression performed on the GPU).
813 *
814 *      Note that currently the image data from the screen is both row-padded
815 *      and the scan lines are reversed.  This routine could be made even
816 *      simpler (faster) if the screen buffer is an array of packed 3-bytes
817 *      per pixels (no padding) and where the origin is the top-left corner.
818 */
819void
820NanoVis::ppmWrite(const char *prefix)
821{
822#define PPM_MAXVAL 255
823    char header[200];
824
825    TRACE("Enter (%dx%d)", winWidth, winHeight);
826    // Generate the PPM binary file header
827    sprintf(header, "P6 %d %d %d\n", winWidth, winHeight, PPM_MAXVAL);
828
829    size_t header_length = strlen(header);
830    size_t data_length = winWidth * winHeight * 3;
831
832    char command[200];
833    sprintf(command, "%s %lu\n", prefix,
834            (unsigned long)header_length + data_length);
835
836    size_t wordsPerRow = (winWidth * 24 + 31) / 32;
837    size_t bytesPerRow = wordsPerRow * 4;
838    size_t rowLength = winWidth * 3;
839    size_t nRecs = winHeight + 2;
840
841    struct iovec *iov;
842    iov = (struct iovec *)malloc(sizeof(struct iovec) * nRecs);
843
844    // Write the nanovisviewer command, then the image header and data.
845    // Command
846    iov[0].iov_base = command;
847    iov[0].iov_len = strlen(command);
848    // Header of image data
849    iov[1].iov_base = header;
850    iov[1].iov_len = header_length;
851    // Image data.
852    int y;
853    unsigned char *srcRowPtr = screenBuffer;
854    for (y = winHeight + 1; y >= 2; y--) {
855        iov[y].iov_base = srcRowPtr;
856        iov[y].iov_len = rowLength;
857        srcRowPtr += bytesPerRow;
858    }
859    if (writev(g_fdOut, iov, nRecs) < 0) {
860        ERROR("write failed: %s", strerror(errno));
861    }
862    free(iov);
863    g_stats.nFrames++;
864    g_stats.nFrameBytes += (bytesPerRow * winHeight);
865    TRACE("Leave (%dx%d)", winWidth, winHeight);
866}
867
868void
869NanoVis::draw3dAxis()
870{
871    glPushAttrib(GL_ENABLE_BIT);
872
873    glDisable(GL_TEXTURE_2D);
874    glEnable(GL_DEPTH_TEST);
875    glEnable(GL_COLOR_MATERIAL);
876    glDisable(GL_BLEND);
877
878    //draw axes
879    GLUquadric *obj;
880
881    obj = gluNewQuadric();
882
883    int segments = 50;
884
885    glColor3f(0.8, 0.8, 0.8);
886    glPushMatrix();
887    glTranslatef(0.4, 0., 0.);
888    glRotatef(90, 1, 0, 0);
889    glRotatef(180, 0, 1, 0);
890    glScalef(0.0005, 0.0005, 0.0005);
891    glutStrokeCharacter(GLUT_STROKE_ROMAN, 'x');
892    glPopMatrix();
893
894    glPushMatrix();
895    glTranslatef(0., 0.4, 0.);
896    glRotatef(90, 1, 0, 0);
897    glRotatef(180, 0, 1, 0);
898    glScalef(0.0005, 0.0005, 0.0005);
899    glutStrokeCharacter(GLUT_STROKE_ROMAN, 'y');
900    glPopMatrix();
901
902    glPushMatrix();
903    glTranslatef(0., 0., 0.4);
904    glRotatef(90, 1, 0, 0);
905    glRotatef(180, 0, 1, 0);
906    glScalef(0.0005, 0.0005, 0.0005);
907    glutStrokeCharacter(GLUT_STROKE_ROMAN, 'z');
908    glPopMatrix();
909
910    glEnable(GL_LIGHTING);
911    glEnable(GL_LIGHT0);
912
913    //glColor3f(0.2, 0.2, 0.8);
914    glPushMatrix();
915    glutSolidSphere(0.02, segments, segments );
916    glPopMatrix();
917
918    glPushMatrix();
919    glRotatef(-90, 1, 0, 0);
920    gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
921    glPopMatrix();
922
923    glPushMatrix();
924    glTranslatef(0., 0.3, 0.);
925    glRotatef(-90, 1, 0, 0);
926    gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
927    glPopMatrix();
928
929    glPushMatrix();
930    glRotatef(90, 0, 1, 0);
931    gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
932    glPopMatrix();
933
934    glPushMatrix();
935    glTranslatef(0.3, 0., 0.);
936    glRotatef(90, 0, 1, 0);
937    gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
938    glPopMatrix();
939
940    glPushMatrix();
941    gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
942    glPopMatrix();
943
944    glPushMatrix();
945    glTranslatef(0., 0., 0.3);
946    gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
947    glPopMatrix();
948
949    gluDeleteQuadric(obj);
950
951    glPopAttrib();
952}
953
954void NanoVis::update()
955{
956    VolumeInterpolator *volInterp = volRenderer->getVolumeInterpolator();
957    if (volInterp->isStarted()) {
958        struct timeval clock;
959        gettimeofday(&clock, NULL);
960        double elapsed_time;
961
962        elapsed_time = clock.tv_sec + clock.tv_usec/1000000.0 -
963            volInterp->getStartTime();
964
965        TRACE("%lf %lf", elapsed_time,
966              volInterp->getInterval());
967        float fraction;
968        float f;
969
970        f = fmod((float) elapsed_time, (float)volInterp->getInterval());
971        if (f == 0.0) {
972            fraction = 0.0f;
973        } else {
974            fraction = f / volInterp->getInterval();
975        }
976        TRACE("fraction : %f", fraction);
977        volInterp->update(fraction);
978    }
979}
980
981void
982NanoVis::setVolumeRanges()
983{
984    double xMin, xMax, yMin, yMax, zMin, zMax, wMin, wMax;
985
986    TRACE("Enter");
987    xMin = yMin = zMin = wMin = DBL_MAX;
988    xMax = yMax = zMax = wMax = -DBL_MAX;
989    VolumeHashmap::iterator itr;
990    for (itr = volumeTable.begin();
991         itr != volumeTable.end(); ++itr) {
992        Volume *volume = itr->second;
993        if (xMin > volume->xAxis.min()) {
994            xMin = volume->xAxis.min();
995        }
996        if (xMax < volume->xAxis.max()) {
997            xMax = volume->xAxis.max();
998        }
999        if (yMin > volume->yAxis.min()) {
1000            yMin = volume->yAxis.min();
1001        }
1002        if (yMax < volume->yAxis.max()) {
1003            yMax = volume->yAxis.max();
1004        }
1005        if (zMin > volume->zAxis.min()) {
1006            zMin = volume->zAxis.min();
1007        }
1008        if (zMax < volume->zAxis.max()) {
1009            zMax = volume->zAxis.max();
1010        }
1011        if (wMin > volume->wAxis.min()) {
1012            wMin = volume->wAxis.min();
1013        }
1014        if (wMax < volume->wAxis.max()) {
1015            wMax = volume->wAxis.max();
1016        }
1017    }
1018    if ((xMin < DBL_MAX) && (xMax > -DBL_MAX)) {
1019        grid->xAxis.setScale(xMin, xMax);
1020    }
1021    if ((yMin < DBL_MAX) && (yMax > -DBL_MAX)) {
1022        grid->yAxis.setScale(yMin, yMax);
1023    }
1024    if ((zMin < DBL_MAX) && (zMax > -DBL_MAX)) {
1025        grid->zAxis.setScale(zMin, zMax);
1026    }
1027    if ((wMin < DBL_MAX) && (wMax > -DBL_MAX)) {
1028        Volume::valueMin = wMin;
1029        Volume::valueMax = wMax;
1030    }
1031    Volume::updatePending = false;
1032    TRACE("Leave");
1033}
1034
1035void
1036NanoVis::setHeightmapRanges()
1037{
1038    double xMin, xMax, yMin, yMax, zMin, zMax, wMin, wMax;
1039
1040    TRACE("Enter");
1041    xMin = yMin = zMin = wMin = DBL_MAX;
1042    xMax = yMax = zMax = wMax = -DBL_MAX;
1043    HeightMapHashmap::iterator itr;
1044    for (itr = heightMapTable.begin();
1045         itr != heightMapTable.end(); ++itr) {
1046        HeightMap *heightMap = itr->second;
1047        if (xMin > heightMap->xAxis.min()) {
1048            xMin = heightMap->xAxis.min();
1049        }
1050        if (xMax < heightMap->xAxis.max()) {
1051            xMax = heightMap->xAxis.max();
1052        }
1053        if (yMin > heightMap->yAxis.min()) {
1054            yMin = heightMap->yAxis.min();
1055        }
1056        if (yMax < heightMap->yAxis.max()) {
1057            yMax = heightMap->yAxis.max();
1058        }
1059        if (zMin > heightMap->zAxis.min()) {
1060            zMin = heightMap->zAxis.min();
1061        }
1062        if (zMax < heightMap->zAxis.max()) {
1063            zMax = heightMap->zAxis.max();
1064        }
1065        if (wMin > heightMap->wAxis.min()) {
1066            wMin = heightMap->wAxis.min();
1067        }
1068        if (wMax < heightMap->wAxis.max()) {
1069            wMax = heightMap->wAxis.max();
1070        }
1071    }
1072    if ((xMin < DBL_MAX) && (xMax > -DBL_MAX)) {
1073        grid->xAxis.setScale(xMin, xMax);
1074    }
1075    if ((yMin < DBL_MAX) && (yMax > -DBL_MAX)) {
1076        grid->yAxis.setScale(yMin, yMax);
1077    }
1078    if ((zMin < DBL_MAX) && (zMax > -DBL_MAX)) {
1079        grid->zAxis.setScale(zMin, zMax);
1080    }
1081    if ((wMin < DBL_MAX) && (wMax > -DBL_MAX)) {
1082        HeightMap::valueMin = grid->yAxis.min();
1083        HeightMap::valueMax = grid->yAxis.max();
1084    }
1085    for (HeightMapHashmap::iterator itr = heightMapTable.begin();
1086         itr != heightMapTable.end(); ++itr) {
1087        itr->second->mapToGrid(grid);
1088    }
1089    HeightMap::updatePending = false;
1090    TRACE("Leave");
1091}
1092
1093void
1094NanoVis::collectBounds(bool onlyVisible)
1095{
1096    if (flags & MAP_FLOWS) {
1097        mapFlows();
1098        grid->xAxis.setScale(xMin, xMax);
1099        grid->yAxis.setScale(yMin, yMax);
1100        grid->zAxis.setScale(zMin, zMax);
1101    }
1102
1103    sceneMin.set(FLT_MAX, FLT_MAX, FLT_MAX);
1104    sceneMax.set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
1105
1106    for (VolumeHashmap::iterator itr = volumeTable.begin();
1107         itr != volumeTable.end(); ++itr) {
1108        Volume *volume = itr->second;
1109
1110        if (onlyVisible && !volume->visible())
1111            continue;
1112
1113        Vector3f bmin, bmax;
1114        volume->getWorldSpaceBounds(bmin, bmax);
1115        if (bmin.x > bmax.x)
1116            continue;
1117
1118        if (sceneMin.x > bmin.x) {
1119            sceneMin.x = bmin.x;
1120        }
1121        if (sceneMax.x < bmax.x) {
1122            sceneMax.x = bmax.x;
1123        }
1124        if (sceneMin.y > bmin.y) {
1125            sceneMin.y = bmin.y;
1126        }
1127        if (sceneMax.y < bmax.y) {
1128            sceneMax.y = bmax.y;
1129        }
1130        if (sceneMin.z > bmin.z) {
1131            sceneMin.z = bmin.z;
1132        }
1133        if (sceneMax.z < bmax.z) {
1134            sceneMax.z = bmax.z;
1135        }
1136    }
1137
1138    for (HeightMapHashmap::iterator itr = heightMapTable.begin();
1139         itr != heightMapTable.end(); ++itr) {
1140        HeightMap *heightMap = itr->second;
1141
1142        if (onlyVisible && !heightMap->isVisible())
1143            continue;
1144
1145        Vector3f bmin, bmax;
1146        heightMap->getWorldSpaceBounds(bmin, bmax);
1147        if (bmin.x > bmax.x)
1148            continue;
1149
1150        if (sceneMin.x > bmin.x) {
1151            sceneMin.x = bmin.x;
1152        }
1153        if (sceneMax.x < bmax.x) {
1154            sceneMax.x = bmax.x;
1155        }
1156        if (sceneMin.y > bmin.y) {
1157            sceneMin.y = bmin.y;
1158        }
1159        if (sceneMax.y < bmax.y) {
1160            sceneMax.y = bmax.y;
1161        }
1162        if (sceneMin.z > bmin.z) {
1163            sceneMin.z = bmin.z;
1164        }
1165        if (sceneMax.z < bmax.z) {
1166            sceneMax.z = bmax.z;
1167        }
1168    }
1169
1170    Vector3f flowMin, flowMax;
1171    getFlowBounds(flowMin, flowMax, onlyVisible);
1172    if (flowMin.x < flowMax.x) {
1173        if (sceneMin.x > flowMin.x) {
1174            sceneMin.x = flowMin.x;
1175        }
1176        if (sceneMax.x < flowMax.x) {
1177            sceneMax.x = flowMax.x;
1178        }
1179        if (sceneMin.y > flowMin.y) {
1180            sceneMin.y = flowMin.y;
1181        }
1182        if (sceneMax.y < flowMax.y) {
1183            sceneMax.y = flowMax.y;
1184        }
1185        if (sceneMin.z > flowMin.z) {
1186            sceneMin.z = flowMin.z;
1187        }
1188        if (sceneMax.z < flowMax.z) {
1189            sceneMax.z = flowMax.z;
1190        }
1191    }
1192
1193    // TODO: Get Grid bounds
1194
1195    if (sceneMin.x > sceneMax.x) {
1196        sceneMin.set(-0.5, -0.5, -0.5);
1197        sceneMax.set( 0.5,  0.5,  0.5);
1198    }
1199
1200    TRACE("Scene bounds: (%g,%g,%g) - (%g,%g,%g)",
1201          sceneMin.x, sceneMin.y, sceneMin.z,
1202          sceneMax.x, sceneMax.y, sceneMax.z);
1203}
1204
1205void
1206NanoVis::setBgColor(float color[3])
1207{
1208    TRACE("Setting bgcolor to %g %g %g", color[0], color[1], color[2]);
1209    glClearColor(color[0], color[1], color[2], 1);
1210}
1211
1212void
1213NanoVis::removeVolume(Volume *volume)
1214{
1215    VolumeHashmap::iterator itr = volumeTable.find(volume->name());
1216    if (itr != volumeTable.end()) {
1217        volumeTable.erase(itr);
1218    }
1219    delete volume;
1220}
1221
1222Flow *
1223NanoVis::getFlow(const char *name)
1224{
1225    FlowHashmap::iterator itr = flowTable.find(name);
1226    if (itr == flowTable.end()) {
1227        TRACE("Can't find flow '%s'", name);
1228        return NULL;
1229    }
1230    return itr->second;
1231}
1232
1233Flow *
1234NanoVis::createFlow(Tcl_Interp *interp, const char *name)
1235{
1236    FlowHashmap::iterator itr = flowTable.find(name);
1237    if (itr != flowTable.end()) {
1238        ERROR("Flow '%s' already exists", name);
1239        return NULL;
1240    }
1241    Flow *flow = new Flow(interp, name);
1242    flowTable[name] = flow;
1243    return flow;
1244}
1245
1246/**
1247 * \brief Delete flow object and hash table entry
1248 *
1249 * This is called by the flow command instance delete callback
1250 */
1251void
1252NanoVis::deleteFlow(const char *name)
1253{
1254    FlowHashmap::iterator itr = flowTable.find(name);
1255    if (itr != flowTable.end()) {
1256        delete itr->second;
1257        flowTable.erase(itr);
1258    }
1259}
1260
1261/**
1262 * \brief Delete all flow object commands
1263 *
1264 * This will also delete the flow objects and hash table entries
1265 */
1266void
1267NanoVis::deleteFlows(Tcl_Interp *interp)
1268{
1269    FlowHashmap::iterator itr;
1270    for (itr = flowTable.begin();
1271         itr != flowTable.end(); ++itr) {
1272        Tcl_DeleteCommandFromToken(interp, itr->second->getCommandToken());
1273    }
1274    flowTable.clear();
1275}
1276
1277bool
1278NanoVis::mapFlows()
1279{
1280    TRACE("Enter");
1281
1282    flags &= ~MAP_FLOWS;
1283
1284    /*
1285     * Step 1. Get the overall min and max magnitudes of all the
1286     *         flow vectors.
1287     */
1288    magMin = DBL_MAX, magMax = -DBL_MAX;
1289
1290    for (FlowHashmap::iterator itr = flowTable.begin();
1291         itr != flowTable.end(); ++itr) {
1292        Flow *flow = itr->second;
1293        double min, max;
1294        if (!flow->isDataLoaded()) {
1295            continue;
1296        }
1297        Rappture::Unirect3d *data = flow->data();
1298        min = data->magMin();
1299        max = data->magMax();
1300        if (min < magMin) {
1301            magMin = min;
1302        }
1303        if (max > magMax) {
1304            magMax = max;
1305        }
1306        if (data->xMin() < xMin) {
1307            xMin = data->xMin();
1308        }
1309        if (data->yMin() < yMin) {
1310            yMin = data->yMin();
1311        }
1312        if (data->zMin() < zMin) {
1313            zMin = data->zMin();
1314        }
1315        if (data->xMax() > xMax) {
1316            xMax = data->xMax();
1317        }
1318        if (data->yMax() > yMax) {
1319            yMax = data->yMax();
1320        }
1321        if (data->zMax() > zMax) {
1322            zMax = data->zMax();
1323        }
1324    }
1325
1326    TRACE("magMin=%g magMax=%g", NanoVis::magMin, NanoVis::magMax);
1327
1328    /*
1329     * Step 2. Generate the vector field from each data set.
1330     */
1331    for (FlowHashmap::iterator itr = flowTable.begin();
1332         itr != flowTable.end(); ++itr) {
1333        Flow *flow = itr->second;
1334        if (!flow->isDataLoaded()) {
1335            continue; // Flow exists, but no data has been loaded yet.
1336        }
1337        if (flow->visible()) {
1338            flow->initializeParticles();
1339        }
1340        if (!flow->scaleVectorField()) {
1341            return false;
1342        }
1343        // FIXME: This doesn't work when there is more than one flow.
1344        licRenderer->setOffset(flow->getRelativePosition());
1345        velocityArrowsSlice->slicePos(flow->getRelativePosition());
1346    }
1347    advectFlows();
1348    return true;
1349}
1350
1351void
1352NanoVis::getFlowBounds(Vector3f& min,
1353                       Vector3f& max,
1354                       bool onlyVisible)
1355{
1356    TRACE("Enter");
1357
1358    min.set(FLT_MAX, FLT_MAX, FLT_MAX);
1359    max.set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
1360
1361    for (FlowHashmap::iterator itr = flowTable.begin();
1362         itr != flowTable.end(); ++itr) {
1363        itr->second->getBounds(min, max, onlyVisible);
1364    }
1365}
1366
1367void
1368NanoVis::renderFlows()
1369{
1370    for (FlowHashmap::iterator itr = flowTable.begin();
1371         itr != flowTable.end(); ++itr) {
1372        Flow *flow = itr->second;
1373        if (flow->isDataLoaded() && flow->visible()) {
1374            flow->render();
1375        }
1376    }
1377    flags &= ~REDRAW_PENDING;
1378}
1379
1380void
1381NanoVis::resetFlows()
1382{
1383    if (licRenderer->active()) {
1384        NanoVis::licRenderer->reset();
1385    }
1386    for (FlowHashmap::iterator itr = flowTable.begin();
1387         itr != flowTable.end(); ++itr) {
1388        Flow *flow = itr->second;
1389        if (flow->isDataLoaded() && flow->visible()) {
1390            flow->resetParticles();
1391        }
1392    }
1393}   
1394
1395void
1396NanoVis::advectFlows()
1397{
1398    for (FlowHashmap::iterator itr = flowTable.begin();
1399         itr != flowTable.end(); ++itr) {
1400        Flow *flow = itr->second;
1401        if (flow->isDataLoaded() && flow->visible()) {
1402            flow->advect();
1403        }
1404    }
1405}
1406
1407void
1408NanoVis::render()
1409{
1410    TRACE("Enter");
1411
1412    if (flags & MAP_FLOWS) {
1413#ifdef notdef
1414        xMin = yMin = zMin = wMin = FLT_MAX, magMin = DBL_MAX;
1415        xMax = yMax = zMax = wMax = -FLT_MAX, magMax = -DBL_MAX;
1416#endif
1417        mapFlows();
1418        grid->xAxis.setScale(xMin, xMax);
1419        grid->yAxis.setScale(yMin, yMax);
1420        grid->zAxis.setScale(zMin, zMax);
1421    }
1422    //assert(glGetError()==0);
1423    if (HeightMap::updatePending) {
1424        setHeightmapRanges();
1425    }
1426    if (Volume::updatePending) {
1427        setVolumeRanges();
1428    }
1429
1430    //start final rendering
1431
1432    // Need to reset fbo since it may have been changed to default (0)
1433    bindOffscreenBuffer();
1434
1435    //clear screen
1436    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1437
1438    glEnable(GL_DEPTH_TEST);
1439    glEnable(GL_COLOR_MATERIAL);
1440
1441    //camera setting activated
1442    cam->initialize();
1443
1444    //set up the orientation of items in the scene.
1445    glPushMatrix();
1446
1447    switch (updir) {
1448    case X_POS:
1449        glRotatef(90, 0, 0, 1);
1450        glRotatef(90, 1, 0, 0);
1451        break;
1452    case Y_POS:
1453        // this is the default
1454        break;
1455    case Z_POS:
1456        glRotatef(-90, 1, 0, 0);
1457        glRotatef(-90, 0, 0, 1);
1458        break;
1459    case X_NEG:
1460        glRotatef(-90, 0, 0, 1);
1461        break;
1462    case Y_NEG:
1463        glRotatef(180, 0, 0, 1);
1464        glRotatef(-90, 0, 1, 0);
1465        break;
1466    case Z_NEG:
1467        glRotatef(90, 1, 0, 0);
1468        break;
1469    }
1470
1471    //now render things in the scene
1472    if (axisOn) {
1473        draw3dAxis();
1474    }
1475    grid->render();
1476    if ((licRenderer != NULL) && (licRenderer->active())) {
1477        licRenderer->render();
1478    }
1479    if ((velocityArrowsSlice != NULL) && (velocityArrowsSlice->enabled())) {
1480        velocityArrowsSlice->render();
1481    }
1482    renderFlows();
1483
1484    volRenderer->renderAll();
1485
1486    HeightMapHashmap::iterator itr;
1487    for (itr = heightMapTable.begin();
1488         itr != heightMapTable.end(); ++itr) {
1489        HeightMap *heightMap = itr->second;
1490        if (heightMap->isVisible()) {
1491            heightMap->render(renderContext);
1492        }
1493    }
1494    glPopMatrix();
1495
1496    CHECK_FRAMEBUFFER_STATUS();
1497    TRACE("Leave");
1498}
1499
Note: See TracBrowser for help on using the repository browser.