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

Last change on this file since 4614 was 4612, checked in by ldelgass, 10 years ago

merge r3597 from trunk

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