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

Last change on this file since 3497 was 3497, checked in by ldelgass, 6 years ago

Rename display() -> render() in nanovis

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