source: nanovis/tags/1.1.3/nanovis.cpp @ 5106

Last change on this file since 5106 was 4820, checked in by ldelgass, 9 years ago

remove dead code

  • Property svn:eol-style set to native
File size: 56.4 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 <assert.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <getopt.h>
22#include <memory.h>
23#include <signal.h>
24#include <sys/resource.h>
25#include <sys/stat.h>
26#include <sys/time.h>
27#include <sys/times.h>
28#include <sys/types.h>
29#include <sys/uio.h> // for readv/writev
30#include <time.h>
31#include <unistd.h>
32
33#include <cstdlib>
34#include <cstdio>
35#include <cstring>
36#include <cmath>
37
38#include <iostream>
39#include <fstream>
40#include <sstream>
41#include <string>
42
43#include <GL/glew.h>
44#include <GL/glut.h>
45
46#include <RpEncode.h>
47
48#include <graphics/RenderContext.h>
49#include <vrmath/Vector3f.h>
50
51#include <util/FilePath.h>
52#include <util/Fonts.h>
53
54#include <BMPImageLoaderImpl.h>
55#include <ImageLoaderFactory.h>
56
57#include "config.h"
58#include "nanovis.h"
59#include "define.h"
60
61#include "Command.h"
62#include "Flow.h"
63#include "Grid.h"
64#include "HeightMap.h"
65#include "NvCamera.h"
66#include "NvShader.h"
67#include "NvLIC.h"
68#include "NvZincBlendeReconstructor.h"
69#include "PlaneRenderer.h"
70#include "Switch.h"
71#include "Trace.h"
72#include "Unirect.h"
73#include "VelocityArrowsSlice.h"
74#include "VolumeInterpolator.h"
75#include "VolumeRenderer.h"
76#include "ZincBlendeVolume.h"
77
78using namespace nv::graphics;
79using namespace nv::util;
80using namespace vrmath;
81
82#define SIZEOF_BMP_HEADER   54
83
84#define CVT2SECS(x)  ((double)(x).tv_sec) + ((double)(x).tv_usec * 1.0e-6)
85
86#define TRUE    1
87#define FALSE   0
88
89// STATIC MEMBER DATA
90
91FILE *NanoVis::stdin = NULL;
92FILE *NanoVis::logfile = NULL;
93FILE *NanoVis::recfile = NULL;
94
95NanoVis::Stats NanoVis::stats;
96int NanoVis::statsFile = -1;
97
98unsigned int NanoVis::flags = 0;
99bool NanoVis::debugFlag = false;
100bool NanoVis::axisOn = true;
101
102int NanoVis::winWidth = NPIX;
103int NanoVis::winHeight = NPIX;
104int NanoVis::renderWindow = 0;
105unsigned char *NanoVis::screenBuffer = NULL;
106Texture2D *NanoVis::legendTexture = NULL;
107Grid *NanoVis::grid = NULL;
108Fonts *NanoVis::fonts;
109int NanoVis::updir = Y_POS;
110NvCamera *NanoVis::cam = NULL;
111RenderContext *NanoVis::renderContext = NULL;
112
113NanoVis::TransferFunctionHashmap NanoVis::tfTable;
114NanoVis::VolumeHashmap NanoVis::volumeTable;
115NanoVis::FlowHashmap NanoVis::flowTable;
116NanoVis::HeightMapHashmap NanoVis::heightMapTable;
117
118double NanoVis::magMin = DBL_MAX;
119double NanoVis::magMax = -DBL_MAX;
120float NanoVis::xMin = FLT_MAX;
121float NanoVis::xMax = -FLT_MAX;
122float NanoVis::yMin = FLT_MAX;
123float NanoVis::yMax = -FLT_MAX;
124float NanoVis::zMin = FLT_MAX;
125float NanoVis::zMax = -FLT_MAX;
126float NanoVis::wMin = FLT_MAX;
127float NanoVis::wMax = -FLT_MAX;
128Vector3f NanoVis::sceneMin, NanoVis::sceneMax;
129
130VolumeRenderer *NanoVis::volRenderer = NULL;
131VelocityArrowsSlice *NanoVis::velocityArrowsSlice = NULL;
132NvLIC *NanoVis::licRenderer = NULL;
133PlaneRenderer *NanoVis::planeRenderer = NULL;
134
135Tcl_Interp *NanoVis::interp;
136
137// Image based flow visualization slice location
138// FLOW
139float NanoVis::_licSlice = 0.5f;
140int NanoVis::_licAxis = 2; // z axis
141
142//frame buffer for final rendering
143GLuint NanoVis::_finalFbo = 0;
144GLuint NanoVis::_finalColorTex = 0;
145GLuint NanoVis::_finalDepthRb = 0;
146
147// Default camera location.
148float def_eye_x = 0.0f;
149float def_eye_y = 0.0f;
150float def_eye_z = 2.5f;
151
152void
153NanoVis::removeAllData()
154{
155    TRACE("Enter");
156    if (grid != NULL) {
157        TRACE("Deleting grid");
158        delete grid;
159    }
160    if (cam != NULL) {
161        TRACE("Deleting cam");
162        delete cam;
163    }
164    if (volRenderer != NULL) {
165        TRACE("Deleting volRenderer");
166        delete volRenderer;
167    }
168    if (planeRenderer != NULL) {
169        TRACE("Deleting planeRenderer");
170        delete planeRenderer;
171    }
172    if (legendTexture != NULL) {
173        TRACE("Deleting legendTexture");
174        delete legendTexture;
175    }
176    TRACE("Deleting flows");
177    deleteFlows(interp);
178    if (licRenderer != NULL) {
179        TRACE("Deleting licRenderer");
180        delete licRenderer;
181    }
182    if (velocityArrowsSlice != NULL) {
183        TRACE("Deleting velocityArrowsSlice");
184        delete velocityArrowsSlice;
185    }
186    if (renderContext != NULL) {
187        TRACE("Deleting renderContext");
188        delete renderContext;
189    }
190    if (screenBuffer != NULL) {
191        TRACE("Deleting screenBuffer");
192        delete [] screenBuffer;
193        screenBuffer = NULL;
194    }
195    if (fonts != NULL) {
196        TRACE("Deleting fonts");
197        delete fonts;
198    }
199    TRACE("Leave");
200}
201
202void
203NanoVis::eventuallyRedraw(unsigned int flag)
204{
205    if (flag) {
206        flags |= flag;
207    }
208    if ((flags & REDRAW_PENDING) == 0) {
209        glutPostRedisplay();
210        flags |= REDRAW_PENDING;
211    }
212}
213
214#ifdef KEEPSTATS
215
216#ifndef STATSDIR
217#define STATSDIR        "/var/tmp/visservers"
218#endif  /*STATSDIR*/
219
220int
221NanoVis::getStatsFile(Tcl_Obj *objPtr)
222{
223    Tcl_DString ds;
224    char fileName[33];
225    char pidstr[200];
226    const char *path;
227    md5_state_t state;
228    md5_byte_t digest[16];
229    char *string;
230    int length;
231
232    if ((objPtr == NULL) || (statsFile >= 0)) {
233        return statsFile;
234    }
235    /* By itself the client's key/value pairs aren't unique.  Add in the
236     * process id of this render server. */
237    sprintf(pidstr, "%ld", (long)stats.pid);
238
239    /* Create an md5 hash of the key/value pairs and use it as the file name. */
240    string = Tcl_GetStringFromObj(objPtr, &length);
241    md5_init(&state);
242    md5_append(&state, (const md5_byte_t *)string, strlen(string));
243    md5_append(&state, (const md5_byte_t *)pidstr, strlen(pidstr));
244    md5_finish(&state, digest);
245    for (int i = 0; i < 16; i++) {
246        sprintf(fileName + i * 2, "%02x", digest[i]);
247    }
248    Tcl_DStringInit(&ds);
249    Tcl_DStringAppend(&ds, STATSDIR, -1);
250    Tcl_DStringAppend(&ds, "/", 1);
251    Tcl_DStringAppend(&ds, fileName, 32);
252    path = Tcl_DStringValue(&ds);
253
254    statsFile = open(path, O_EXCL | O_CREAT | O_WRONLY, 0600);
255    Tcl_DStringFree(&ds);
256    if (statsFile < 0) {
257        ERROR("can't open \"%s\": %s", fileName, strerror(errno));
258        return -1;
259    }
260    return statsFile;
261}
262
263int
264NanoVis::writeToStatsFile(int f, const char *s, size_t length)
265{
266    if (f >= 0) {
267        ssize_t numWritten;
268
269        numWritten = write(f, s, length);
270        if (numWritten == (ssize_t)length) {
271            close(dup(f));
272        }
273    }
274    return 0;
275}
276
277static int
278serverStats(int code)
279{
280    double start, finish;
281    char buf[BUFSIZ];
282    Tcl_DString ds;
283    int result;
284
285    {
286        struct timeval tv;
287
288        /* Get ending time.  */
289        gettimeofday(&tv, NULL);
290        finish = CVT2SECS(tv);
291        tv = NanoVis::stats.start;
292        start = CVT2SECS(tv);
293    }
294    /*
295     * Session information:
296     *   - Name of render server
297     *   - Process ID
298     *   - Hostname where server is running
299     *   - Start date of session
300     *   - Start date of session in seconds
301     *   - Number of frames returned
302     *   - Number of bytes total returned (in frames)
303     *   - Number of commands received
304     *   - Total elapsed time of all commands
305     *   - Total elapsed time of session
306     *   - Exit code of vizserver
307     *   - User time
308     *   - System time
309     *   - User time of children
310     *   - System time of children
311     */
312
313    Tcl_DStringInit(&ds);
314   
315    Tcl_DStringAppendElement(&ds, "render_stop");
316    /* renderer */
317    Tcl_DStringAppendElement(&ds, "renderer");
318    Tcl_DStringAppendElement(&ds, "nanovis");
319    /* pid */
320    Tcl_DStringAppendElement(&ds, "pid");
321    sprintf(buf, "%d", NanoVis::stats.pid);
322    Tcl_DStringAppendElement(&ds, buf);
323    /* host */
324    Tcl_DStringAppendElement(&ds, "host");
325    gethostname(buf, BUFSIZ-1);
326    buf[BUFSIZ-1] = '\0';
327    Tcl_DStringAppendElement(&ds, buf);
328    /* date */
329    Tcl_DStringAppendElement(&ds, "date");
330    strcpy(buf, ctime(&NanoVis::stats.start.tv_sec));
331    buf[strlen(buf) - 1] = '\0';
332    Tcl_DStringAppendElement(&ds, buf);
333    /* date_secs */
334    Tcl_DStringAppendElement(&ds, "date_secs");
335    sprintf(buf, "%ld", NanoVis::stats.start.tv_sec);
336    Tcl_DStringAppendElement(&ds, buf);
337    /* num_frames */
338    Tcl_DStringAppendElement(&ds, "num_frames");
339    sprintf(buf, "%lu", (unsigned long)NanoVis::stats.nFrames);
340    Tcl_DStringAppendElement(&ds, buf);
341    /* frame_bytes */
342    Tcl_DStringAppendElement(&ds, "frame_bytes");
343    sprintf(buf, "%lu", (unsigned long)NanoVis::stats.nBytes);
344    Tcl_DStringAppendElement(&ds, buf);
345    /* num_commands */
346    Tcl_DStringAppendElement(&ds, "num_commands");
347    sprintf(buf, "%lu", (unsigned long)NanoVis::stats.nCommands);
348    Tcl_DStringAppendElement(&ds, buf);
349    /* cmd_time */
350    Tcl_DStringAppendElement(&ds, "cmd_time");
351    sprintf(buf, "%g", NanoVis::stats.cmdTime);
352    Tcl_DStringAppendElement(&ds, buf);
353    /* session_time */
354    Tcl_DStringAppendElement(&ds, "session_time");
355    sprintf(buf, "%g", finish - start);
356    Tcl_DStringAppendElement(&ds, buf);
357    /* status */
358    Tcl_DStringAppendElement(&ds, "status");
359    sprintf(buf, "%d", code);
360    Tcl_DStringAppendElement(&ds, buf);
361    {
362        long clocksPerSec = sysconf(_SC_CLK_TCK);
363        double clockRes = 1.0 / clocksPerSec;
364        struct tms tms;
365
366        memset(&tms, 0, sizeof(tms));
367        times(&tms);
368        /* utime */
369        Tcl_DStringAppendElement(&ds, "utime");
370        sprintf(buf, "%g", tms.tms_utime * clockRes);
371        Tcl_DStringAppendElement(&ds, buf);
372        /* stime */
373        Tcl_DStringAppendElement(&ds, "stime");
374        sprintf(buf, "%g", tms.tms_stime * clockRes);
375        Tcl_DStringAppendElement(&ds, buf);
376        /* cutime */
377        Tcl_DStringAppendElement(&ds, "cutime");
378        sprintf(buf, "%g", tms.tms_cutime * clockRes);
379        Tcl_DStringAppendElement(&ds, buf);
380        /* cstime */
381        Tcl_DStringAppendElement(&ds, "cstime");
382        sprintf(buf, "%g", tms.tms_cstime * clockRes);
383        Tcl_DStringAppendElement(&ds, buf);
384    }
385    Tcl_DStringAppend(&ds, "\n", -1);
386    int f = NanoVis::getStatsFile(NULL);
387    result = NanoVis::writeToStatsFile(f, Tcl_DStringValue(&ds),
388                                       Tcl_DStringLength(&ds));
389    close(f);
390    Tcl_DStringFree(&ds);
391    return result;
392}
393
394#endif
395
396static void
397initService()
398{
399    TRACE("Enter");
400
401    const char* user = getenv("USER");
402    char* logName = NULL;
403    int logNameLen = 0;
404
405    if (user == NULL) {
406        logNameLen = 20+1;
407        logName = (char *)calloc(logNameLen, sizeof(char));
408        strncpy(logName, "/tmp/nanovis_log.txt", logNameLen);
409    } else {
410        logNameLen = 17+1+strlen(user);
411        logName = (char *)calloc(logNameLen, sizeof(char));
412        strncpy(logName, "/tmp/nanovis_log_", logNameLen);
413        strncat(logName, user, strlen(user));
414    }
415
416    //open log and map stderr to log file
417    NanoVis::logfile = fopen(logName, "w");
418    dup2(fileno(NanoVis::logfile), 2);
419    /* dup2(2,1); */
420
421    // clean up malloc'd memory
422    if (logName != NULL) {
423        free(logName);
424    }
425
426    TRACE("Leave");
427}
428
429static void
430exitService(int code)
431{
432    TRACE("Enter: %d", code);
433
434    NanoVis::removeAllData();
435
436    NvShader::exitCg();
437
438    //close log file
439    if (NanoVis::logfile != NULL) {
440        fclose(NanoVis::logfile);
441        NanoVis::logfile = NULL;
442    }
443
444#ifdef KEEPSTATS
445    serverStats(code);
446#endif
447    closelog();
448
449    exit(code);
450}
451
452static int
453executeCommand(Tcl_Interp *interp, Tcl_DString *dsPtr)
454{
455    struct timeval tv;
456    double start, finish;
457    int result;
458
459#ifdef WANT_TRACE
460    char *str = Tcl_DStringValue(dsPtr);
461    std::string cmd(str);
462    cmd.erase(cmd.find_last_not_of(" \n\r\t")+1);
463    TRACE("command %lu: '%s'", NanoVis::stats.nCommands+1, cmd.c_str());
464#endif
465
466    gettimeofday(&tv, NULL);
467    start = CVT2SECS(tv);
468
469    if (NanoVis::recfile != NULL) {
470        fprintf(NanoVis::recfile, "%s", Tcl_DStringValue(dsPtr));
471        fflush(NanoVis::recfile);
472    }
473    result = Tcl_Eval(interp, Tcl_DStringValue(dsPtr));
474    Tcl_DStringSetLength(dsPtr, 0);
475
476    gettimeofday(&tv, NULL);
477    finish = CVT2SECS(tv);
478
479    NanoVis::stats.cmdTime += finish - start;
480    NanoVis::stats.nCommands++;
481    TRACE("Leave status=%d", result);
482    return result;
483}
484
485void
486NanoVis::pan(float dx, float dy)
487{
488    /* Move the camera and its target by equal amounts along the x and y
489     * axes. */
490    TRACE("pan: x=%f, y=%f", dx, dy);
491
492    cam->x(def_eye_x - dx);
493    cam->y(def_eye_y + dy);
494    TRACE("set eye to %f %f", cam->x(), cam->y());
495}
496
497void
498NanoVis::zoom(float z)
499{
500    /* Move the camera and its target by equal amounts along the x and y
501     * axes. */
502    TRACE("zoom: z=%f", z);
503
504    cam->z(def_eye_z / z);
505
506    collectBounds();
507    cam->resetClippingRange(sceneMin, sceneMax);
508
509    TRACE("set cam z to %f", cam->z());
510}
511
512void
513NanoVis::resetCamera(bool resetOrientation)
514{
515    TRACE("Resetting all=%d", resetOrientation ? 1 : 0);
516
517    collectBounds();
518    cam->reset(sceneMin, sceneMax, resetOrientation);
519
520    def_eye_x = cam->x();
521    def_eye_y = cam->y();
522    def_eye_z = cam->z();
523}
524
525/** \brief Load a 3D volume
526 *
527 * \param name Volume ID
528 * \param width Number of samples in X direction
529 * \param height Number of samples in Y direction
530 * \param depth Number of samples in Z direction
531 * \param numComponents the number of scalars for each space point. All component
532 * scalars for a point are placed consequtively in data array
533 * width, height and depth: number of points in each dimension
534 * \param data Array of floats
535 * \param vmin Min value of field
536 * \param vmax Max value of field
537 * \param nonZeroMin Minimum non-zero value of field
538 * \param data pointer to an array of floats.
539 */
540Volume *
541NanoVis::loadVolume(const char *name, int width, int height, int depth,
542                    int numComponents, float *data, double vmin, double vmax,
543                    double nonZeroMin)
544{
545    VolumeHashmap::iterator itr = volumeTable.find(name);
546    if (itr != volumeTable.end()) {
547        WARN("volume \"%s\" already exists", name);
548        removeVolume(itr->second);
549    }
550
551    Volume *volume = new Volume(0.f, 0.f, 0.f,
552                                width, height, depth,
553                                numComponents,
554                                data, vmin, vmax, nonZeroMin);
555    Volume::updatePending = true;
556    volume->name(name);
557    volumeTable[name] = volume;
558
559    return volume;
560}
561
562// Gets a colormap 1D texture by name.
563TransferFunction *
564NanoVis::getTransferFunction(const TransferFunctionId& id)
565{
566    TransferFunctionHashmap::iterator itr = tfTable.find(id);
567    if (itr == tfTable.end()) {
568        TRACE("No transfer function named \"%s\" found", id.c_str());
569        return NULL;
570    } else {
571        return itr->second;
572    }
573}
574
575// Creates of updates a colormap 1D texture by name.
576TransferFunction *
577NanoVis::defineTransferFunction(const TransferFunctionId& id,
578                                size_t n, float *data)
579{
580    TransferFunction *tf = getTransferFunction(id);
581    if (tf == NULL) {
582        TRACE("Creating new transfer function \"%s\"", id.c_str());
583        tf = new TransferFunction(id.c_str(), n, data);
584        tfTable[id] = tf;
585    } else {
586        TRACE("Updating existing transfer function \"%s\"", id.c_str());
587        tf->update(n, data);
588    }
589    return tf;
590}
591
592int
593NanoVis::renderLegend(TransferFunction *tf, double min, double max,
594                      int width, int height, const char *volArg)
595{
596    TRACE("Enter");
597
598    int old_width = winWidth;
599    int old_height = winHeight;
600
601    planeRenderer->setScreenSize(width, height);
602    resizeOffscreenBuffer(width, height);
603
604    // generate data for the legend
605    float data[512];
606    for (int i=0; i < 256; i++) {
607        data[i] = data[i+256] = (float)(i/255.0);
608    }
609    legendTexture = new Texture2D(256, 2, GL_FLOAT, GL_LINEAR, 1, data);
610    int index = planeRenderer->addPlane(legendTexture, tf);
611    planeRenderer->setActivePlane(index);
612
613    bindOffscreenBuffer();
614    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear screen
615    planeRenderer->render();
616
617    // INSOO
618    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, screenBuffer);
619    //glReadPixels(0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, screenBuffer); // INSOO's
620
621    {
622        char prefix[200];
623        ssize_t nWritten;
624
625        TRACE("Sending ppm legend image %s min:%g max:%g", volArg, min, max);
626        sprintf(prefix, "nv>legend %s %g %g", volArg, min, max);
627        ppmWrite(prefix);
628        nWritten = write(1, "\n", 1);
629        assert(nWritten == 1);
630    }
631    planeRenderer->removePlane(index);
632    resizeOffscreenBuffer(old_width, old_height);
633
634    delete legendTexture;
635    legendTexture = NULL;
636    TRACE("Leave");
637    return TCL_OK;
638}
639
640//initialize frame buffer objects for offscreen rendering
641void
642NanoVis::initOffscreenBuffer()
643{
644    TRACE("Enter");
645    assert(_finalFbo == 0);
646    // Initialize a fbo for final display.
647    glGenFramebuffersEXT(1, &_finalFbo);
648
649    glGenTextures(1, &_finalColorTex);
650    glBindTexture(GL_TEXTURE_2D, _finalColorTex);
651
652    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
653    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
654    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
655
656#if defined(HAVE_FLOAT_TEXTURES) && defined(USE_HALF_FLOAT)
657    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, winWidth, winHeight, 0,
658                 GL_RGB, GL_INT, NULL);
659#elif defined(HAVE_FLOAT_TEXTURES)
660    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, winWidth, winHeight, 0,
661                 GL_RGB, GL_INT, NULL);
662#else
663    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, winWidth, winHeight, 0,
664                 GL_RGB, GL_INT, NULL);
665#endif
666
667    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _finalFbo);
668    glGenRenderbuffersEXT(1, &_finalDepthRb);
669    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
670    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24,
671                             winWidth, winHeight);
672    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
673                              GL_TEXTURE_2D, _finalColorTex, 0);
674    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
675                                 GL_RENDERBUFFER_EXT, _finalDepthRb);
676
677    GLenum status;
678    if (!CheckFBO(&status)) {
679        PrintFBOStatus(status, "finalFbo");
680        exitService(3);
681    }
682
683    TRACE("Leave");
684}
685
686//resize the offscreen buffer
687void
688NanoVis::resizeOffscreenBuffer(int w, int h)
689{
690    TRACE("Enter (%d, %d)", w, h);
691    if ((w == winWidth) && (h == winHeight)) {
692        return;
693    }
694    winWidth = w;
695    winHeight = h;
696
697    if (fonts) {
698        fonts->resize(w, h);
699    }
700    TRACE("screenBuffer size: %d %d", w, h);
701
702    if (screenBuffer != NULL) {
703        delete [] screenBuffer;
704        screenBuffer = NULL;
705    }
706
707    screenBuffer = new unsigned char[4*winWidth*winHeight];
708    assert(screenBuffer != NULL);
709   
710    //delete the current render buffer resources
711    glDeleteTextures(1, &_finalColorTex);
712    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
713    glDeleteRenderbuffersEXT(1, &_finalDepthRb);
714
715    TRACE("before deleteframebuffers");
716    glDeleteFramebuffersEXT(1, &_finalFbo);
717
718    TRACE("reinitialize FBO");
719    //Reinitialize final fbo for final display
720    glGenFramebuffersEXT(1, &_finalFbo);
721
722    glGenTextures(1, &_finalColorTex);
723    glBindTexture(GL_TEXTURE_2D, _finalColorTex);
724
725    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
726    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
727    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
728
729#if defined(HAVE_FLOAT_TEXTURES) && defined(USE_HALF_FLOAT)
730    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, winWidth, winHeight, 0,
731                 GL_RGB, GL_INT, NULL);
732#elif defined(HAVE_FLOAT_TEXTURES)
733    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, winWidth, winHeight, 0,
734                 GL_RGB, GL_INT, NULL);
735#else
736    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, winWidth, winHeight, 0,
737                 GL_RGB, GL_INT, NULL);
738#endif
739
740    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _finalFbo);
741    glGenRenderbuffersEXT(1, &_finalDepthRb);
742    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _finalDepthRb);
743    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24,
744                             winWidth, winHeight);
745    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
746                              GL_TEXTURE_2D, _finalColorTex, 0);
747    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
748                                 GL_RENDERBUFFER_EXT, _finalDepthRb);
749
750    GLenum status;
751    if (!CheckFBO(&status)) {
752        PrintFBOStatus(status, "finalFbo");
753        exitService(3);
754    }
755
756    TRACE("change camera");
757    //change the camera setting
758    cam->setScreenSize(0, 0, winWidth, winHeight);
759    planeRenderer->setScreenSize(winWidth, winHeight);
760
761    TRACE("Leave (%d, %d)", w, h);
762}
763
764static
765void cgErrorCallback(void)
766{
767    if (!NvShader::printErrorInfo()) {
768        TRACE("Cg error, exiting...");
769        exitService(-1);
770    }
771}
772
773void NanoVis::init(const char* path)
774{
775    // print OpenGL driver information
776    TRACE("-----------------------------------------------------------");
777    TRACE("OpenGL version: %s", glGetString(GL_VERSION));
778    TRACE("OpenGL vendor: %s", glGetString(GL_VENDOR));
779    TRACE("OpenGL renderer: %s", glGetString(GL_RENDERER));
780    TRACE("-----------------------------------------------------------");
781
782    if (path == NULL) {
783        ERROR("No path defined for shaders or resources");
784        exitService(1);
785    }
786    GLenum err = glewInit();
787    if (GLEW_OK != err) {
788        ERROR("Can't init GLEW: %s", glewGetErrorString(err));
789        exitService(1);
790    }
791    TRACE("Using GLEW %s", glewGetString(GLEW_VERSION));
792
793    // OpenGL 2.1 includes VBOs, PBOs, MRT, NPOT textures, point parameters, point sprites,
794    // GLSL 1.2, and occlusion queries.
795    if (!GLEW_VERSION_2_1) {
796        ERROR("OpenGL version 2.1 or greater is required");
797        exitService(1);
798    }
799
800    // NVIDIA driver may report OpenGL 2.1, but not support PBOs in
801    // indirect GLX contexts
802    if (!GLEW_ARB_pixel_buffer_object) {
803        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).");
804        exitService(1);
805    }
806
807    // Additional extensions not in 2.1 core
808
809    // Framebuffer objects were promoted in 3.0
810    if (!GLEW_EXT_framebuffer_object) {
811        ERROR("EXT_framebuffer_oject extension is required");
812        exitService(1);
813    }
814    // Rectangle textures were promoted in 3.1
815    // FIXME: can use NPOT in place of rectangle textures
816    if (!GLEW_ARB_texture_rectangle) {
817        ERROR("ARB_texture_rectangle extension is required");
818        exitService(1);
819    }
820#ifdef HAVE_FLOAT_TEXTURES
821    // Float color buffers and textures were promoted in 3.0
822    if (!GLEW_ARB_texture_float ||
823        !GLEW_ARB_color_buffer_float) {
824        ERROR("ARB_texture_float and ARB_color_buffer_float extensions are required");
825        exitService(1);
826    }
827#endif
828    // FIXME: should use ARB programs or (preferably) a GLSL profile for portability
829    if (!GLEW_NV_vertex_program3 ||
830        !GLEW_NV_fragment_program2) {
831        ERROR("NV_vertex_program3 and NV_fragment_program2 extensions are required");
832        exitService(1);
833    }
834
835    if (!FilePath::getInstance()->setPath(path)) {
836        ERROR("can't set file path to %s", path);
837        exitService(1);
838    }
839
840    ImageLoaderFactory::getInstance()->addLoaderImpl("bmp", new BMPImageLoaderImpl());
841
842    NvShader::initCg();
843    NvShader::setErrorCallback(cgErrorCallback);
844
845    fonts = new Fonts();
846    fonts->addFont("verdana", "verdana.fnt");
847    fonts->setFont("verdana");
848
849    velocityArrowsSlice = new VelocityArrowsSlice;
850    licRenderer = new NvLIC(NMESH, NPIX, NPIX, _licAxis, _licSlice);
851
852    grid = new Grid();
853    grid->setFont(fonts);
854}
855
856void
857NanoVis::initGL()
858{
859    TRACE("in initGL");
860    //buffer to store data read from the screen
861    if (screenBuffer) {
862        delete[] screenBuffer;
863        screenBuffer = NULL;
864    }
865    screenBuffer = new unsigned char[4*winWidth*winHeight];
866    assert(screenBuffer != NULL);
867
868    //create the camera with default setting
869    cam = new NvCamera(0, 0, winWidth, winHeight,
870                       def_eye_x, def_eye_y, def_eye_z);
871
872    glEnable(GL_TEXTURE_2D);
873    glShadeModel(GL_FLAT);
874    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
875    glClearColor(0, 0, 0, 1);
876    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
877
878    //initialize lighting
879    GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
880    GLfloat mat_shininess[] = {30.0};
881    GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};
882    GLfloat green_light[] = {0.1, 0.5, 0.1, 1.0};
883
884    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
885    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
886    glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
887    glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
888    glLightfv(GL_LIGHT1, GL_DIFFUSE, green_light);
889    glLightfv(GL_LIGHT1, GL_SPECULAR, white_light);
890
891    initOffscreenBuffer();    //frame buffer object for offscreen rendering
892
893    //create volume renderer
894    volRenderer = new VolumeRenderer();
895
896    // create
897    renderContext = new RenderContext();
898
899    //create a 2D plane renderer
900    planeRenderer = new PlaneRenderer(winWidth, winHeight);
901
902    //assert(glGetError()==0);
903
904    TRACE("leaving initGL");
905}
906
907// used internally to build up the BMP file header
908// Writes an integer value into the header data structure at pos
909static inline void
910bmpHeaderAddInt(unsigned char* header, int& pos, int data)
911{
912#ifdef WORDS_BIGENDIAN
913    header[pos++] = (data >> 24) & 0xFF;
914    header[pos++] = (data >> 16) & 0xFF;
915    header[pos++] = (data >> 8)  & 0xFF;
916    header[pos++] = (data)       & 0xFF;
917#else
918    header[pos++] = data & 0xff;
919    header[pos++] = (data >> 8) & 0xff;
920    header[pos++] = (data >> 16) & 0xff;
921    header[pos++] = (data >> 24) & 0xff;
922#endif
923}
924
925// INSOO
926// FOR DEBUGGING
927void
928NanoVis::bmpWriteToFile(int frame_number, const char *directory_name)
929{
930    unsigned char header[SIZEOF_BMP_HEADER];
931    int pos = 0;
932    header[pos++] = 'B';
933    header[pos++] = 'M';
934
935    // BE CAREFUL:  BMP files must have an even multiple of 4 bytes
936    // on each scan line.  If need be, we add padding to each line.
937    int pad = 0;
938    if ((3*winWidth) % 4 > 0) {
939        pad = 4 - ((3*winWidth) % 4);
940    }
941
942    // file size in bytes
943    int fsize = (3*winWidth+pad)*winHeight + SIZEOF_BMP_HEADER;
944    bmpHeaderAddInt(header, pos, fsize);
945
946    // reserved value (must be 0)
947    bmpHeaderAddInt(header, pos, 0);
948
949    // offset in bytes to start of bitmap data
950    bmpHeaderAddInt(header, pos, SIZEOF_BMP_HEADER);
951
952    // size of the BITMAPINFOHEADER
953    bmpHeaderAddInt(header, pos, 40);
954
955    // width of the image in pixels
956    bmpHeaderAddInt(header, pos, winWidth);
957
958    // height of the image in pixels
959    bmpHeaderAddInt(header, pos, winHeight);
960
961    // 1 plane + (24 bits/pixel << 16)
962    bmpHeaderAddInt(header, pos, 1572865);
963
964    // no compression
965    // size of image for compression
966    bmpHeaderAddInt(header, pos, 0);
967    bmpHeaderAddInt(header, pos, 0);
968
969    // x pixels per meter
970    // y pixels per meter
971    bmpHeaderAddInt(header, pos, 0);
972    bmpHeaderAddInt(header, pos, 0);
973
974    // number of colors used (0 = compute from bits/pixel)
975    // number of important colors (0 = all colors important)
976    bmpHeaderAddInt(header, pos, 0);
977    bmpHeaderAddInt(header, pos, 0);
978
979    // BE CAREFUL: BMP format wants BGR ordering for screen data
980    unsigned char* scr = screenBuffer;
981    for (int row=0; row < winHeight; row++) {
982        for (int col=0; col < winWidth; col++) {
983            unsigned char tmp = scr[2];
984            scr[2] = scr[0];  // B
985            scr[0] = tmp;     // R
986            scr += 3;
987        }
988        scr += pad;  // skip over padding already in screen data
989    }
990
991    FILE* f;
992    char filename[100];
993    if (frame_number >= 0) {
994        if (directory_name)
995            sprintf(filename, "%s/image%03d.bmp", directory_name, frame_number);
996        else
997            sprintf(filename, "/tmp/flow_animation/image%03d.bmp", frame_number);
998
999        TRACE("Writing %s", filename);
1000        f = fopen(filename, "wb");
1001        if (f == 0) {
1002            ERROR("cannot create file");
1003        }
1004    } else {
1005        f = fopen("/tmp/image.bmp", "wb");
1006        if (f == 0) {
1007            ERROR("cannot create file");
1008        }
1009    }
1010    if (fwrite(header, SIZEOF_BMP_HEADER, 1, f) != 1) {
1011        ERROR("can't write header: short write");
1012    }
1013    if (fwrite(screenBuffer, (3*winWidth+pad)*winHeight, 1, f) != 1) {
1014        ERROR("can't write data: short write");
1015    }
1016    fclose(f);
1017}
1018
1019void
1020NanoVis::bmpWrite(const char *prefix)
1021{
1022    unsigned char header[SIZEOF_BMP_HEADER];
1023    ssize_t nWritten;
1024    int pos = 0;
1025
1026    // BE CAREFUL:  BMP files must have an even multiple of 4 bytes
1027    // on each scan line.  If need be, we add padding to each line.
1028    int pad = 0;
1029    if ((3*winWidth) % 4 > 0) {
1030        pad = 4 - ((3*winWidth) % 4);
1031    }
1032    pad = 0;
1033    int fsize = (3*winWidth+pad)*winHeight + sizeof(header);
1034
1035    char string[200];
1036    sprintf(string, "%s %d\n", prefix, fsize);
1037    nWritten = write(1, string, strlen(string));
1038    assert(nWritten == (ssize_t)strlen(string));
1039    header[pos++] = 'B';
1040    header[pos++] = 'M';
1041
1042    // file size in bytes
1043    bmpHeaderAddInt(header, pos, fsize);
1044
1045    // reserved value (must be 0)
1046    bmpHeaderAddInt(header, pos, 0);
1047
1048    // offset in bytes to start of bitmap data
1049    bmpHeaderAddInt(header, pos, SIZEOF_BMP_HEADER);
1050
1051    // size of the BITMAPINFOHEADER
1052    bmpHeaderAddInt(header, pos, 40);
1053
1054    // width of the image in pixels
1055    bmpHeaderAddInt(header, pos, winWidth);
1056
1057    // height of the image in pixels
1058    bmpHeaderAddInt(header, pos, winHeight);
1059
1060    // 1 plane + (24 bits/pixel << 16)
1061    bmpHeaderAddInt(header, pos, 1572865);
1062
1063    // no compression
1064    // size of image for compression
1065    bmpHeaderAddInt(header, pos, 0);
1066    bmpHeaderAddInt(header, pos, 0);
1067
1068    // x pixels per meter
1069    // y pixels per meter
1070    bmpHeaderAddInt(header, pos, 0);
1071    bmpHeaderAddInt(header, pos, 0);
1072
1073    // number of colors used (0 = compute from bits/pixel)
1074    // number of important colors (0 = all colors important)
1075    bmpHeaderAddInt(header, pos, 0);
1076    bmpHeaderAddInt(header, pos, 0);
1077
1078    // BE CAREFUL: BMP format wants BGR ordering for screen data
1079    unsigned char* scr = screenBuffer;
1080    for (int row=0; row < winHeight; row++) {
1081        for (int col=0; col < winWidth; col++) {
1082            unsigned char tmp = scr[2];
1083            scr[2] = scr[0];  // B
1084            scr[0] = tmp;     // R
1085            scr += 3;
1086        }
1087        scr += pad;  // skip over padding already in screen data
1088    }
1089
1090    nWritten = write(1, header, SIZEOF_BMP_HEADER);
1091    assert(nWritten == SIZEOF_BMP_HEADER);
1092    nWritten = write(1, screenBuffer, (3*winWidth+pad)*winHeight);
1093    assert(nWritten == (3*winWidth+pad)*winHeight);
1094    stats.nFrames++;
1095    stats.nBytes += (3*winWidth+pad)*winHeight;
1096}
1097
1098/*
1099 * ppmWrite --
1100 *
1101 *  Writes the screen image as PPM binary data to the nanovisviewer
1102 *  client.  The PPM binary format is very simple.
1103 *
1104 *      P6 w h 255\n
1105 *      3-byte RGB pixel data.
1106 *
1107 *  The nanovisviewer client (using the TkImg library) will do less work
1108 *  to unpack this format, as opposed to BMP or PNG.  (This doesn't
1109 *  eliminate the need to look into DXT compression performed on the GPU).
1110 *
1111 *      Note that currently the image data from the screen is both row-padded
1112 *      and the scan lines are reversed.  This routine could be made even
1113 *      simpler (faster) if the screen buffer is an array of packed 3-bytes
1114 *      per pixels (no padding) and where the origin is the top-left corner.
1115 */
1116void
1117NanoVis::ppmWrite(const char *prefix)
1118{
1119#define PPM_MAXVAL 255
1120    char header[200];
1121
1122    TRACE("Enter (%dx%d)", winWidth, winHeight);
1123    // Generate the PPM binary file header
1124    sprintf(header, "P6 %d %d %d\n", winWidth, winHeight, PPM_MAXVAL);
1125
1126    size_t header_length = strlen(header);
1127    size_t data_length = winWidth * winHeight * 3;
1128
1129    char command[200];
1130    sprintf(command, "%s %lu\n", prefix,
1131            (unsigned long)header_length + data_length);
1132
1133    size_t wordsPerRow = (winWidth * 24 + 31) / 32;
1134    size_t bytesPerRow = wordsPerRow * 4;
1135    size_t rowLength = winWidth * 3;
1136    size_t nRecs = winHeight + 2;
1137
1138    struct iovec *iov;
1139    iov = (struct iovec *)malloc(sizeof(struct iovec) * nRecs);
1140
1141    // Write the nanovisviewer command, then the image header and data.
1142    // Command
1143    iov[0].iov_base = command;
1144    iov[0].iov_len = strlen(command);
1145    // Header of image data
1146    iov[1].iov_base = header;
1147    iov[1].iov_len = header_length;
1148    // Image data.
1149    int y;
1150    unsigned char *srcRowPtr = screenBuffer;
1151    for (y = winHeight + 1; y >= 2; y--) {
1152        iov[y].iov_base = srcRowPtr;
1153        iov[y].iov_len = rowLength;
1154        srcRowPtr += bytesPerRow;
1155    }
1156    if (writev(1, iov, nRecs) < 0) {
1157        ERROR("write failed: %s", strerror(errno));
1158    }
1159    free(iov);
1160    stats.nFrames++;
1161    stats.nBytes += (bytesPerRow * winHeight);
1162    TRACE("Leave (%dx%d)", winWidth, winHeight);
1163}
1164
1165void
1166NanoVis::sendDataToClient(const char *command, const char *data, size_t dlen)
1167{
1168    size_t numRecords = 2;
1169
1170    struct iovec *iov = new iovec[numRecords];
1171
1172    // Write the nanovisviewer command, then the image header and data.
1173    // Command
1174    // FIXME: shouldn't have to cast this
1175    iov[0].iov_base = (char *)command;
1176    iov[0].iov_len = strlen(command);
1177    // Data
1178    // FIXME: shouldn't have to cast this
1179    iov[1].iov_base = (char *)data;
1180    iov[1].iov_len = dlen;
1181    if (writev(1, iov, numRecords) < 0) {
1182        ERROR("write failed: %s", strerror(errno));
1183    }
1184    delete [] iov;
1185}
1186
1187void
1188NanoVis::idle()
1189{
1190    TRACE("Enter");
1191
1192    glutSetWindow(renderWindow);
1193
1194    processCommands();
1195
1196    TRACE("Leave");
1197}
1198
1199void
1200NanoVis::draw3dAxis()
1201{
1202    glPushAttrib(GL_ENABLE_BIT);
1203
1204    glDisable(GL_TEXTURE_2D);
1205    glEnable(GL_DEPTH_TEST);
1206    glEnable(GL_COLOR_MATERIAL);
1207    glDisable(GL_BLEND);
1208
1209    //draw axes
1210    GLUquadric *obj;
1211
1212    obj = gluNewQuadric();
1213
1214    int segments = 50;
1215
1216    glColor3f(0.8, 0.8, 0.8);
1217    glPushMatrix();
1218    glTranslatef(0.4, 0., 0.);
1219    glRotatef(90, 1, 0, 0);
1220    glRotatef(180, 0, 1, 0);
1221    glScalef(0.0005, 0.0005, 0.0005);
1222    glutStrokeCharacter(GLUT_STROKE_ROMAN, 'x');
1223    glPopMatrix();
1224
1225    glPushMatrix();
1226    glTranslatef(0., 0.4, 0.);
1227    glRotatef(90, 1, 0, 0);
1228    glRotatef(180, 0, 1, 0);
1229    glScalef(0.0005, 0.0005, 0.0005);
1230    glutStrokeCharacter(GLUT_STROKE_ROMAN, 'y');
1231    glPopMatrix();
1232
1233    glPushMatrix();
1234    glTranslatef(0., 0., 0.4);
1235    glRotatef(90, 1, 0, 0);
1236    glRotatef(180, 0, 1, 0);
1237    glScalef(0.0005, 0.0005, 0.0005);
1238    glutStrokeCharacter(GLUT_STROKE_ROMAN, 'z');
1239    glPopMatrix();
1240
1241    glEnable(GL_LIGHTING);
1242    glEnable(GL_LIGHT0);
1243
1244    //glColor3f(0.2, 0.2, 0.8);
1245    glPushMatrix();
1246    glutSolidSphere(0.02, segments, segments );
1247    glPopMatrix();
1248
1249    glPushMatrix();
1250    glRotatef(-90, 1, 0, 0);
1251    gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
1252    glPopMatrix();
1253
1254    glPushMatrix();
1255    glTranslatef(0., 0.3, 0.);
1256    glRotatef(-90, 1, 0, 0);
1257    gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
1258    glPopMatrix();
1259
1260    glPushMatrix();
1261    glRotatef(90, 0, 1, 0);
1262    gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
1263    glPopMatrix();
1264
1265    glPushMatrix();
1266    glTranslatef(0.3, 0., 0.);
1267    glRotatef(90, 0, 1, 0);
1268    gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
1269    glPopMatrix();
1270
1271    glPushMatrix();
1272    gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
1273    glPopMatrix();
1274
1275    glPushMatrix();
1276    glTranslatef(0., 0., 0.3);
1277    gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
1278    glPopMatrix();
1279
1280    gluDeleteQuadric(obj);
1281
1282    glPopAttrib();
1283}
1284
1285void NanoVis::update()
1286{
1287    VolumeInterpolator *volInterp = volRenderer->getVolumeInterpolator();
1288    if (volInterp->isStarted()) {
1289        struct timeval clock;
1290        gettimeofday(&clock, NULL);
1291        double elapsed_time;
1292
1293        elapsed_time = clock.tv_sec + clock.tv_usec/1000000.0 -
1294            volInterp->getStartTime();
1295
1296        TRACE("%lf %lf", elapsed_time,
1297              volInterp->getInterval());
1298        float fraction;
1299        float f;
1300
1301        f = fmod((float) elapsed_time, (float)volInterp->getInterval());
1302        if (f == 0.0) {
1303            fraction = 0.0f;
1304        } else {
1305            fraction = f / volInterp->getInterval();
1306        }
1307        TRACE("fraction : %f", fraction);
1308        volInterp->update(fraction);
1309    }
1310}
1311
1312void
1313NanoVis::setVolumeRanges()
1314{
1315    double xMin, xMax, yMin, yMax, zMin, zMax, wMin, wMax;
1316
1317    TRACE("Enter");
1318    xMin = yMin = zMin = wMin = DBL_MAX;
1319    xMax = yMax = zMax = wMax = -DBL_MAX;
1320    VolumeHashmap::iterator itr;
1321    for (itr = volumeTable.begin();
1322         itr != volumeTable.end(); ++itr) {
1323        Volume *volume = itr->second;
1324        if (xMin > volume->xAxis.min()) {
1325            xMin = volume->xAxis.min();
1326        }
1327        if (xMax < volume->xAxis.max()) {
1328            xMax = volume->xAxis.max();
1329        }
1330        if (yMin > volume->yAxis.min()) {
1331            yMin = volume->yAxis.min();
1332        }
1333        if (yMax < volume->yAxis.max()) {
1334            yMax = volume->yAxis.max();
1335        }
1336        if (zMin > volume->zAxis.min()) {
1337            zMin = volume->zAxis.min();
1338        }
1339        if (zMax < volume->zAxis.max()) {
1340            zMax = volume->zAxis.max();
1341        }
1342        if (wMin > volume->wAxis.min()) {
1343            wMin = volume->wAxis.min();
1344        }
1345        if (wMax < volume->wAxis.max()) {
1346            wMax = volume->wAxis.max();
1347        }
1348    }
1349    if ((xMin < DBL_MAX) && (xMax > -DBL_MAX)) {
1350        grid->xAxis.setScale(xMin, xMax);
1351    }
1352    if ((yMin < DBL_MAX) && (yMax > -DBL_MAX)) {
1353        grid->yAxis.setScale(yMin, yMax);
1354    }
1355    if ((zMin < DBL_MAX) && (zMax > -DBL_MAX)) {
1356        grid->zAxis.setScale(zMin, zMax);
1357    }
1358    if ((wMin < DBL_MAX) && (wMax > -DBL_MAX)) {
1359        Volume::valueMin = wMin;
1360        Volume::valueMax = wMax;
1361    }
1362    Volume::updatePending = false;
1363    TRACE("Leave");
1364}
1365
1366void
1367NanoVis::setHeightmapRanges()
1368{
1369    double xMin, xMax, yMin, yMax, zMin, zMax, wMin, wMax;
1370
1371    TRACE("Enter");
1372    xMin = yMin = zMin = wMin = DBL_MAX;
1373    xMax = yMax = zMax = wMax = -DBL_MAX;
1374    HeightMapHashmap::iterator itr;
1375    for (itr = heightMapTable.begin();
1376         itr != heightMapTable.end(); ++itr) {
1377        HeightMap *heightMap = itr->second;
1378        if (xMin > heightMap->xAxis.min()) {
1379            xMin = heightMap->xAxis.min();
1380        }
1381        if (xMax < heightMap->xAxis.max()) {
1382            xMax = heightMap->xAxis.max();
1383        }
1384        if (yMin > heightMap->yAxis.min()) {
1385            yMin = heightMap->yAxis.min();
1386        }
1387        if (yMax < heightMap->yAxis.max()) {
1388            yMax = heightMap->yAxis.max();
1389        }
1390        if (zMin > heightMap->zAxis.min()) {
1391            zMin = heightMap->zAxis.min();
1392        }
1393        if (zMax < heightMap->zAxis.max()) {
1394            zMax = heightMap->zAxis.max();
1395        }
1396        if (wMin > heightMap->wAxis.min()) {
1397            wMin = heightMap->wAxis.min();
1398        }
1399        if (wMax < heightMap->wAxis.max()) {
1400            wMax = heightMap->wAxis.max();
1401        }
1402    }
1403    if ((xMin < DBL_MAX) && (xMax > -DBL_MAX)) {
1404        grid->xAxis.setScale(xMin, xMax);
1405    }
1406    if ((yMin < DBL_MAX) && (yMax > -DBL_MAX)) {
1407        grid->yAxis.setScale(yMin, yMax);
1408    }
1409    if ((zMin < DBL_MAX) && (zMax > -DBL_MAX)) {
1410        grid->zAxis.setScale(zMin, zMax);
1411    }
1412    if ((wMin < DBL_MAX) && (wMax > -DBL_MAX)) {
1413        HeightMap::valueMin = grid->yAxis.min();
1414        HeightMap::valueMax = grid->yAxis.max();
1415    }
1416    for (HeightMapHashmap::iterator itr = heightMapTable.begin();
1417         itr != heightMapTable.end(); ++itr) {
1418        itr->second->mapToGrid(grid);
1419    }
1420    HeightMap::updatePending = false;
1421    TRACE("Leave");
1422}
1423
1424void
1425NanoVis::collectBounds(bool onlyVisible)
1426{
1427    if (flags & MAP_FLOWS) {
1428        mapFlows();
1429        grid->xAxis.setScale(xMin, xMax);
1430        grid->yAxis.setScale(yMin, yMax);
1431        grid->zAxis.setScale(zMin, zMax);
1432    }
1433
1434    sceneMin.set(FLT_MAX, FLT_MAX, FLT_MAX);
1435    sceneMax.set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
1436
1437    for (VolumeHashmap::iterator itr = volumeTable.begin();
1438         itr != volumeTable.end(); ++itr) {
1439        Volume *volume = itr->second;
1440
1441        if (onlyVisible && !volume->visible())
1442            continue;
1443
1444        Vector3f bmin, bmax;
1445        volume->getWorldSpaceBounds(bmin, bmax);
1446        if (bmin.x > bmax.x)
1447            continue;
1448
1449        if (sceneMin.x > bmin.x) {
1450            sceneMin.x = bmin.x;
1451        }
1452        if (sceneMax.x < bmax.x) {
1453            sceneMax.x = bmax.x;
1454        }
1455        if (sceneMin.y > bmin.y) {
1456            sceneMin.y = bmin.y;
1457        }
1458        if (sceneMax.y < bmax.y) {
1459            sceneMax.y = bmax.y;
1460        }
1461        if (sceneMin.z > bmin.z) {
1462            sceneMin.z = bmin.z;
1463        }
1464        if (sceneMax.z < bmax.z) {
1465            sceneMax.z = bmax.z;
1466        }
1467    }
1468
1469    for (HeightMapHashmap::iterator itr = heightMapTable.begin();
1470         itr != heightMapTable.end(); ++itr) {
1471        HeightMap *heightMap = itr->second;
1472
1473        if (onlyVisible && !heightMap->isVisible())
1474            continue;
1475
1476        Vector3f bmin, bmax;
1477        heightMap->getWorldSpaceBounds(bmin, bmax);
1478        if (bmin.x > bmax.x)
1479            continue;
1480
1481        if (sceneMin.x > bmin.x) {
1482            sceneMin.x = bmin.x;
1483        }
1484        if (sceneMax.x < bmax.x) {
1485            sceneMax.x = bmax.x;
1486        }
1487        if (sceneMin.y > bmin.y) {
1488            sceneMin.y = bmin.y;
1489        }
1490        if (sceneMax.y < bmax.y) {
1491            sceneMax.y = bmax.y;
1492        }
1493        if (sceneMin.z > bmin.z) {
1494            sceneMin.z = bmin.z;
1495        }
1496        if (sceneMax.z < bmax.z) {
1497            sceneMax.z = bmax.z;
1498        }
1499    }
1500
1501    Vector3f flowMin, flowMax;
1502    getFlowBounds(flowMin, flowMax, onlyVisible);
1503    if (flowMin.x < flowMax.x) {
1504        if (sceneMin.x > flowMin.x) {
1505            sceneMin.x = flowMin.x;
1506        }
1507        if (sceneMax.x < flowMax.x) {
1508            sceneMax.x = flowMax.x;
1509        }
1510        if (sceneMin.y > flowMin.y) {
1511            sceneMin.y = flowMin.y;
1512        }
1513        if (sceneMax.y < flowMax.y) {
1514            sceneMax.y = flowMax.y;
1515        }
1516        if (sceneMin.z > flowMin.z) {
1517            sceneMin.z = flowMin.z;
1518        }
1519        if (sceneMax.z < flowMax.z) {
1520            sceneMax.z = flowMax.z;
1521        }
1522    }
1523
1524    // TODO: Get Grid bounds
1525
1526    if (sceneMin.x > sceneMax.x) {
1527        sceneMin.set(-0.5, -0.5, -0.5);
1528        sceneMax.set( 0.5,  0.5,  0.5);
1529    }
1530
1531    TRACE("Scene bounds: (%g,%g,%g) - (%g,%g,%g)",
1532          sceneMin.x, sceneMin.y, sceneMin.z,
1533          sceneMax.x, sceneMax.y, sceneMax.z);
1534}
1535
1536void
1537NanoVis::setBgColor(float color[3])
1538{
1539    TRACE("Setting bgcolor to %g %g %g", color[0], color[1], color[2]);
1540    glClearColor(color[0], color[1], color[2], 1);
1541}
1542
1543Flow *
1544NanoVis::getFlow(const char *name)
1545{
1546    FlowHashmap::iterator itr = flowTable.find(name);
1547    if (itr == flowTable.end()) {
1548        TRACE("Can't find flow '%s'", name);
1549        return NULL;
1550    }
1551    return itr->second;
1552}
1553
1554Flow *
1555NanoVis::createFlow(Tcl_Interp *interp, const char *name)
1556{
1557    FlowHashmap::iterator itr = flowTable.find(name);
1558    if (itr != flowTable.end()) {
1559        ERROR("Flow '%s' already exists", name);
1560        return NULL;
1561    }
1562    Flow *flow = new Flow(interp, name);
1563    flowTable[name] = flow;
1564    return flow;
1565}
1566
1567/**
1568 * \brief Delete flow object and hash table entry
1569 *
1570 * This is called by the flow command instance delete callback
1571 */
1572void
1573NanoVis::deleteFlow(const char *name)
1574{
1575    FlowHashmap::iterator itr = flowTable.find(name);
1576    if (itr != flowTable.end()) {
1577        delete itr->second;
1578        flowTable.erase(itr);
1579    }
1580}
1581
1582/**
1583 * \brief Delete all flow object commands
1584 *
1585 * This will also delete the flow objects and hash table entries
1586 */
1587void
1588NanoVis::deleteFlows(Tcl_Interp *interp)
1589{
1590    FlowHashmap::iterator itr;
1591    for (itr = flowTable.begin();
1592         itr != flowTable.end(); ++itr) {
1593        Tcl_DeleteCommandFromToken(interp, itr->second->getCommandToken());
1594    }
1595    flowTable.clear();
1596}
1597
1598bool
1599NanoVis::mapFlows()
1600{
1601    TRACE("Enter");
1602
1603    flags &= ~MAP_FLOWS;
1604
1605    /*
1606     * Step 1. Get the overall min and max magnitudes of all the
1607     *         flow vectors.
1608     */
1609    magMin = DBL_MAX, magMax = -DBL_MAX;
1610
1611    for (FlowHashmap::iterator itr = flowTable.begin();
1612         itr != flowTable.end(); ++itr) {
1613        Flow *flow = itr->second;
1614        double min, max;
1615        if (!flow->isDataLoaded()) {
1616            continue;
1617        }
1618        Rappture::Unirect3d *data = flow->data();
1619        min = data->magMin();
1620        max = data->magMax();
1621        if (min < magMin) {
1622            magMin = min;
1623        }
1624        if (max > magMax) {
1625            magMax = max;
1626        }
1627        if (data->xMin() < xMin) {
1628            xMin = data->xMin();
1629        }
1630        if (data->yMin() < yMin) {
1631            yMin = data->yMin();
1632        }
1633        if (data->zMin() < zMin) {
1634            zMin = data->zMin();
1635        }
1636        if (data->xMax() > xMax) {
1637            xMax = data->xMax();
1638        }
1639        if (data->yMax() > yMax) {
1640            yMax = data->yMax();
1641        }
1642        if (data->zMax() > zMax) {
1643            zMax = data->zMax();
1644        }
1645    }
1646
1647    TRACE("magMin=%g magMax=%g", NanoVis::magMin, NanoVis::magMax);
1648
1649    /*
1650     * Step 2. Generate the vector field from each data set.
1651     */
1652    for (FlowHashmap::iterator itr = flowTable.begin();
1653         itr != flowTable.end(); ++itr) {
1654        Flow *flow = itr->second;
1655        if (!flow->isDataLoaded()) {
1656            continue; // Flow exists, but no data has been loaded yet.
1657        }
1658        if (flow->visible()) {
1659            flow->initializeParticles();
1660        }
1661        if (!flow->scaleVectorField()) {
1662            return false;
1663        }
1664        // FIXME: This doesn't work when there is more than one flow.
1665        licRenderer->setOffset(flow->getRelativePosition());
1666        velocityArrowsSlice->slicePos(flow->getRelativePosition());
1667    }
1668    advectFlows();
1669    return true;
1670}
1671
1672void
1673NanoVis::getFlowBounds(Vector3f& min,
1674                       Vector3f& max,
1675                       bool onlyVisible)
1676{
1677    TRACE("Enter");
1678
1679    min.set(FLT_MAX, FLT_MAX, FLT_MAX);
1680    max.set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
1681
1682    for (FlowHashmap::iterator itr = flowTable.begin();
1683         itr != flowTable.end(); ++itr) {
1684        itr->second->getBounds(min, max, onlyVisible);
1685    }
1686}
1687
1688void
1689NanoVis::renderFlows()
1690{
1691    for (FlowHashmap::iterator itr = flowTable.begin();
1692         itr != flowTable.end(); ++itr) {
1693        Flow *flow = itr->second;
1694        if (flow->isDataLoaded() && flow->visible()) {
1695            flow->render();
1696        }
1697    }
1698    flags &= ~REDRAW_PENDING;
1699}
1700
1701void
1702NanoVis::resetFlows()
1703{
1704    if (licRenderer->active()) {
1705        NanoVis::licRenderer->reset();
1706    }
1707    for (FlowHashmap::iterator itr = flowTable.begin();
1708         itr != flowTable.end(); ++itr) {
1709        Flow *flow = itr->second;
1710        if (flow->isDataLoaded() && flow->visible()) {
1711            flow->resetParticles();
1712        }
1713    }
1714}   
1715
1716void
1717NanoVis::advectFlows()
1718{
1719    for (FlowHashmap::iterator itr = flowTable.begin();
1720         itr != flowTable.end(); ++itr) {
1721        Flow *flow = itr->second;
1722        if (flow->isDataLoaded() && flow->visible()) {
1723            flow->advect();
1724        }
1725    }
1726}
1727
1728void
1729NanoVis::render()
1730{
1731    TRACE("Enter");
1732
1733    if (flags & MAP_FLOWS) {
1734#ifdef notdef
1735        xMin = yMin = zMin = wMin = FLT_MAX, magMin = DBL_MAX;
1736        xMax = yMax = zMax = wMax = -FLT_MAX, magMax = -DBL_MAX;
1737#endif
1738        mapFlows();
1739        grid->xAxis.setScale(xMin, xMax);
1740        grid->yAxis.setScale(yMin, yMax);
1741        grid->zAxis.setScale(zMin, zMax);
1742    }
1743    //assert(glGetError()==0);
1744    if (HeightMap::updatePending) {
1745        setHeightmapRanges();
1746    }
1747    if (Volume::updatePending) {
1748        setVolumeRanges();
1749    }
1750
1751    //start final rendering
1752
1753    // Need to reset fbo since it may have been changed to default (0)
1754    bindOffscreenBuffer();
1755
1756    //clear screen
1757    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1758
1759    glEnable(GL_DEPTH_TEST);
1760    glEnable(GL_COLOR_MATERIAL);
1761
1762    //camera setting activated
1763    cam->initialize();
1764
1765    //set up the orientation of items in the scene.
1766    glPushMatrix();
1767
1768    switch (updir) {
1769    case X_POS:
1770        glRotatef(90, 0, 0, 1);
1771        glRotatef(90, 1, 0, 0);
1772        break;
1773    case Y_POS:
1774        // this is the default
1775        break;
1776    case Z_POS:
1777        glRotatef(-90, 1, 0, 0);
1778        glRotatef(-90, 0, 0, 1);
1779        break;
1780    case X_NEG:
1781        glRotatef(-90, 0, 0, 1);
1782        break;
1783    case Y_NEG:
1784        glRotatef(180, 0, 0, 1);
1785        glRotatef(-90, 0, 1, 0);
1786        break;
1787    case Z_NEG:
1788        glRotatef(90, 1, 0, 0);
1789        break;
1790    }
1791
1792    //now render things in the scene
1793    if (axisOn) {
1794        draw3dAxis();
1795    }
1796    if (grid->isVisible()) {
1797        grid->render();
1798    }
1799    if ((licRenderer != NULL) && (licRenderer->active())) {
1800        licRenderer->render();
1801    }
1802    if ((velocityArrowsSlice != NULL) && (velocityArrowsSlice->enabled())) {
1803        velocityArrowsSlice->render();
1804    }
1805    if (!flowTable.empty()) {
1806        renderFlows();
1807    }
1808
1809    volRenderer->renderAll();
1810
1811    TRACE("Render heightmaps");
1812    HeightMapHashmap::iterator itr;
1813    for (itr = heightMapTable.begin();
1814         itr != heightMapTable.end(); ++itr) {
1815        HeightMap *heightMap = itr->second;
1816        if (heightMap->isVisible()) {
1817            heightMap->render(renderContext);
1818        }
1819    }
1820    glPopMatrix();
1821
1822    CHECK_FRAMEBUFFER_STATUS();
1823    TRACE("Leave");
1824}
1825
1826void
1827NanoVis::processCommands()
1828{
1829    flags &= ~REDRAW_PENDING;
1830
1831    TRACE("Enter");
1832
1833    int flags = fcntl(0, F_GETFL, 0);
1834    fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
1835
1836    int status = TCL_OK;
1837
1838    //  Read and execute as many commands as we can from stdin...
1839    Tcl_DString cmdbuffer;
1840    Tcl_DStringInit(&cmdbuffer);
1841    int nCommands = 0;
1842    bool isComplete = false;
1843    while ((!feof(NanoVis::stdin)) && (status == TCL_OK)) {
1844        //
1845        //  Read the next command from the buffer.  First time through we
1846        //  block here and wait if necessary until a command comes in.
1847        //
1848        //  BE CAREFUL: Read only one command, up to a newline.  The "volume
1849        //  data follows" command needs to be able to read the data
1850        //  immediately following the command, and we shouldn't consume it
1851        //  here.
1852        //
1853        while (!feof(NanoVis::stdin)) {
1854            int c = fgetc(NanoVis::stdin);
1855            char ch;
1856            if (c <= 0) {
1857                if (errno == EWOULDBLOCK) {
1858                    break;
1859                }
1860                exitService(100);
1861            }
1862            ch = (char)c;
1863            Tcl_DStringAppend(&cmdbuffer, &ch, 1);
1864            if (ch == '\n') {
1865                isComplete = Tcl_CommandComplete(Tcl_DStringValue(&cmdbuffer));
1866                if (isComplete) {
1867                    break;
1868                }
1869            }
1870        }
1871        // no command? then we're done for now
1872        if (Tcl_DStringLength(&cmdbuffer) == 0) {
1873            break;
1874        }
1875        if (isComplete) {
1876            // back to original flags during command evaluation...
1877            fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
1878            status = executeCommand(interp, &cmdbuffer);
1879            // non-blocking for next read -- we might not get anything
1880            fcntl(0, F_SETFL, flags | O_NONBLOCK);
1881            isComplete = false;
1882            nCommands++;
1883            CHECK_FRAMEBUFFER_STATUS();
1884        }
1885    }
1886    fcntl(0, F_SETFL, flags);
1887
1888    if (status != TCL_OK) {
1889        char *msg;
1890        char hdr[200];
1891        int msgSize, hdrSize;
1892        Tcl_Obj *objPtr;
1893
1894        objPtr = Tcl_GetObjResult(interp);
1895        msg = Tcl_GetStringFromObj(objPtr, &msgSize);
1896        hdrSize = sprintf(hdr, "nv>viserror -type internal_error -bytes %d\n", msgSize);
1897        {
1898            struct iovec iov[2];
1899
1900            iov[0].iov_base = hdr;
1901            iov[0].iov_len = hdrSize;
1902            iov[1].iov_base = msg;
1903            iov[1].iov_len = msgSize;
1904            if (writev(1, iov, 2) < 0) {
1905                ERROR("write failed: %s", strerror(errno));
1906            }
1907        }
1908        TRACE("Leaving on ERROR");
1909        return;
1910    }
1911    if (feof(NanoVis::stdin)) {
1912        TRACE("Exiting server on EOF from client");
1913        exitService(90);
1914    }
1915
1916    update();
1917
1918    bindOffscreenBuffer();  //enable offscreen render
1919    render();
1920    readScreen();
1921
1922    if (feof(NanoVis::stdin)) {
1923        exitService(90);
1924    }
1925
1926    ppmWrite("nv>image -type image -bytes");
1927
1928    TRACE("Leave");
1929}
1930
1931int
1932main(int argc, char **argv)
1933{
1934    const char *path;
1935    char *newPath;
1936
1937    newPath = NULL;
1938    path = NULL;
1939    NanoVis::stdin = stdin;
1940
1941    fprintf(stdout, "NanoVis %s (build %s)\n", NANOVIS_VERSION, SVN_VERSION);
1942    fflush(stdout);
1943
1944    openlog("nanovis", LOG_CONS | LOG_PERROR | LOG_PID, LOG_USER);
1945    memset(&NanoVis::stats, 0, sizeof(NanoVis::Stats));
1946    NanoVis::stats.pid = getpid();
1947    gettimeofday(&NanoVis::stats.start, NULL);
1948
1949    /* Initialize GLUT here so it can parse and remove GLUT-specific
1950     * command-line options before we parse the command-line below. */
1951    glutInit(&argc, argv);
1952    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
1953    glutInitWindowSize(NanoVis::winWidth, NanoVis::winHeight);
1954    glutInitWindowPosition(10, 10);
1955    NanoVis::renderWindow = glutCreateWindow("nanovis");
1956    glutIdleFunc(NanoVis::idle);
1957
1958    glutDisplayFunc(NanoVis::render);
1959    glutReshapeFunc(NanoVis::resizeOffscreenBuffer);
1960
1961    while (1) {
1962        static struct option long_options[] = {
1963            {"infile",  required_argument, NULL, 0},
1964            {"path",    required_argument, NULL, 2},
1965            {"debug",   no_argument,       NULL, 3},
1966            {"record",  required_argument, NULL, 4},
1967            {0, 0, 0, 0}
1968        };
1969        int option_index = 0;
1970        int c;
1971
1972        c = getopt_long(argc, argv, ":dp:i:l:r:", long_options, &option_index);
1973        if (c == -1) {
1974            break;
1975        }
1976        switch (c) {
1977        case '?':
1978            fprintf(stderr, "unknown option -%c\n", optopt);
1979            return 1;
1980        case ':':
1981            if (optopt < 4) {
1982                fprintf(stderr, "argument missing for --%s option\n",
1983                        long_options[optopt].name);
1984            } else {
1985                fprintf(stderr, "argument missing for -%c option\n", optopt);
1986            }
1987            return 1;
1988        case 2:
1989        case 'p':
1990            path = optarg;
1991            break;
1992        case 3:
1993        case 'd':
1994            NanoVis::debugFlag = true;
1995            break;
1996        case 0:
1997        case 'i':
1998            NanoVis::stdin = fopen(optarg, "r");
1999            if (NanoVis::stdin == NULL) {
2000                perror(optarg);
2001                return 2;
2002            }
2003            break;
2004        case 4:
2005        case 'r':
2006            Tcl_DString ds;
2007            char buf[200];
2008
2009            Tcl_DStringInit(&ds);
2010            Tcl_DStringAppend(&ds, optarg, -1);
2011            sprintf(buf, ".%d", getpid());
2012            Tcl_DStringAppend(&ds, buf, -1);
2013            NanoVis::recfile = fopen(Tcl_DStringValue(&ds), "w");
2014            if (NanoVis::recfile == NULL) {
2015                perror(optarg);
2016                return 2;
2017            }
2018            break;
2019        default:
2020            fprintf(stderr,"unknown option '%c'.\n", c);
2021            return 1;
2022        }
2023    }     
2024    if (path == NULL) {
2025        char *p;
2026
2027        // See if we can derive the path from the location of the program.
2028        // Assume program is in the form <path>/bin/nanovis.
2029        path = argv[0];
2030        p = strrchr((char *)path, '/');
2031        if (p != NULL) {
2032            *p = '\0';
2033            p = strrchr((char *)path, '/');
2034        }
2035        if (p == NULL) {
2036            TRACE("path not specified");
2037            return 1;
2038        }
2039        *p = '\0';
2040        newPath = new char[(strlen(path) + 15) * 2 + 1];
2041        sprintf(newPath, "%s/lib/shaders:%s/lib/resources", path, path);
2042        path = newPath;
2043    }
2044
2045    FilePath::getInstance()->setWorkingDirectory(argc, (const char**) argv);
2046
2047#ifdef notdef
2048    signal(SIGPIPE, SIG_IGN);
2049#endif
2050    initService();
2051
2052    NanoVis::init(path);
2053    if (newPath != NULL) {
2054        delete [] newPath;
2055    }
2056    NanoVis::initGL();
2057
2058    NanoVis::interp = initTcl();
2059
2060    NanoVis::resizeOffscreenBuffer(NanoVis::winWidth, NanoVis::winHeight);
2061
2062    glutMainLoop();
2063
2064    exitService(80);
2065}
2066
2067void
2068NanoVis::removeVolume(Volume *volume)
2069{
2070    VolumeHashmap::iterator itr = volumeTable.find(volume->name());
2071    if (itr != volumeTable.end()) {
2072        volumeTable.erase(itr);
2073    }
2074    delete volume;
2075}
Note: See TracBrowser for help on using the repository browser.