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

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

merge r3596 from trunk

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