source: trunk/packages/vizservers/nanovis/nanovis.cpp @ 3362

Last change on this file since 3362 was 3362, checked in by ldelgass, 11 years ago

Merge nanovis2 branch to trunk

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