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

Last change on this file since 3568 was 3568, checked in by ldelgass, 8 years ago

Fix crash on getting transfer function name, remove unused plane command.

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