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

Last change on this file since 3567 was 3567, checked in by ldelgass, 12 years ago

Refactor and cleanups in nanovis, mainly to switch to using STL hash tables
(TR1 required) instead of Tcl hash tables, split out Flow particles and boxes
to separate implementation files. The goal is to achieve better separation of
Tcl command parsing and the core graphics rendering objects and code.

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