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

Last change on this file since 3464 was 3463, checked in by ldelgass, 7 years ago

Begin process of renaming R2 library

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