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

Last change on this file since 4923 was 4904, checked in by ldelgass, 5 years ago

Merge serveral changes from trunk. Does not include threading, world space
changes, etc.

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