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

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

drop int from long int

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