source: trunk/gui/vizservers/nanovis/nanovis.cpp @ 469

Last change on this file since 469 was 469, checked in by mmc, 18 years ago

Fixed nanovis server to handle the flavor of dx files used by BioMOCA.

File size: 92.8 KB
Line 
1/*
2 * ----------------------------------------------------------------------
3 * Nanovis: Visualization of Nanoelectronics Data
4 *
5 * ======================================================================
6 *  AUTHOR:  Wei Qiao <qiaow@purdue.edu>
7 *           Michael McLennan <mmclennan@purdue.edu>
8 *           Purdue Rendering and Perceptualization Lab (PURPL)
9 *
10 *  Copyright (c) 2004-2006  Purdue Research Foundation
11 *
12 *  See the file "license.terms" for information on usage and
13 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 * ======================================================================
15 */
16
17#include <stdio.h>
18#include <math.h>
19#include <fstream>
20#include <iostream>
21#include <sstream>
22#include <string>
23#include <sys/time.h>
24#include <sys/types.h>
25#include <unistd.h>
26#include <fcntl.h>
27
28#include "nanovis.h"
29#include "RpField1D.h"
30#include "RpFieldRect3D.h"
31#include "RpFieldPrism3D.h"
32
33//transfer function headers
34#include "transfer-function/TransferFunctionMain.h"
35#include "transfer-function/ControlPoint.h"
36#include "transfer-function/TransferFunctionGLUTWindow.h"
37#include "transfer-function/ColorGradientGLUTWindow.h"
38#include "transfer-function/ColorPaletteWindow.h"
39#include "transfer-function/MainWindow.h"
40
41//render server
42
43VolumeRenderer* vol_render;
44PlaneRenderer* plane_render;
45Camera* cam;
46
47//if true nanovis renders volumes in 3D, if not renders 2D plane
48bool volume_mode = true;
49
50// color table for built-in transfer function editor
51float color_table[256][4];     
52
53// default transfer function
54char *def_transfunc = "transfunc define default {\n\
55  0.0  1 1 1\n\
56  0.2  1 1 0\n\
57  0.4  0 1 0\n\
58  0.6  0 1 1\n\
59  0.8  0 0 1\n\
60  1.0  1 0 1\n\
61} {\n\
62  0.00  1.0\n\
63  0.05  0.0\n\
64  0.15  0.0\n\
65  0.20  1.0\n\
66  0.25  0.0\n\
67  0.35  0.0\n\
68  0.40  1.0\n\
69  0.45  0.0\n\
70  0.55  0.0\n\
71  0.60  1.0\n\
72  0.65  0.0\n\
73  0.75  0.0\n\
74  0.80  1.0\n\
75  0.85  0.0\n\
76  0.95  0.0\n\
77  1.00  1.0\n\
78}";
79
80#ifdef XINETD
81FILE* xinetd_log;
82#endif
83
84FILE* event_log;
85//log
86void init_event_log();
87void end_event_log();
88double cur_time;        //in seconds
89double get_time_interval();
90
91int render_window;              //the handle of the render window;
92// forward declarations
93void init_particles();
94void get_slice_vectors();
95Rappture::Outcome load_volume_file(int index, char *fname);
96void load_volume(int index, int width, int height, int depth, int n_component, float* data, double vmin, double vmax);
97TransferFunction* get_transfunc(char *name);
98void resize_offscreen_buffer(int w, int h);
99void offscreen_buffer_capture();
100void bmp_header_add_int(unsigned char* header, int& pos, int data);
101void bmp_write(const char* cmd);
102
103ParticleSystem* psys;
104float psys_x=0.4, psys_y=0, psys_z=0;
105
106Lic* lic;
107
108//frame buffer for final rendering
109unsigned char* screen_buffer = NULL;
110NVISid final_fbo, final_color_tex, final_depth_rb;
111
112bool advect=false;
113float vert[NMESH*NMESH*3];              //particle positions in main memory
114float slice_vector[NMESH*NMESH*4];      //per slice vectors in main memory
115
116int n_volumes = 0;
117// pointers to volumes, currently handle up to 10 volumes
118vector<Volume*> volume;
119
120// maps transfunc name to TransferFunction object
121Tcl_HashTable tftable;
122
123// pointers to 2D planes, currently handle up 10
124Texture2D* plane[10];
125
126// value indicates "up" axis:  x=1, y=2, z=3, -x=-1, -y=-2, -z=-3
127int updir = 2;
128
129PerfQuery* perf;                        //perfromance counter
130
131//Nvidia CG shaders and their parameters
132CGcontext g_context;
133
134CGprogram m_passthru_fprog;
135CGparameter m_passthru_scale_param, m_passthru_bias_param;
136
137CGprogram m_copy_texcoord_fprog;
138
139CGprogram m_one_volume_fprog;
140CGparameter m_vol_one_volume_param;
141CGparameter m_tf_one_volume_param;
142CGparameter m_mvi_one_volume_param;
143CGparameter m_mv_one_volume_param;
144CGparameter m_render_param_one_volume_param;
145
146CGprogram m_vert_std_vprog;
147CGparameter m_mvp_vert_std_param;
148CGparameter m_mvi_vert_std_param;
149
150using namespace std;
151
152
153// Tcl interpreter for incoming messages
154static Tcl_Interp *interp;
155static Tcl_DString cmdbuffer;
156
157static int CameraCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]));
158static int CutplaneCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]));
159static int LegendCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]));
160static int ScreenCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]));
161static int TransfuncCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]));
162static int UpCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]));
163static int VolumeCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]));
164
165static int PlaneNewCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]));
166static int PlaneLinkCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]));
167static int PlaneEnableCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]));
168
169static int GetVolumeIndices _ANSI_ARGS_((Tcl_Interp *interp, int argc, CONST84 char *argv[], vector<int>* vectorPtr));
170static int GetAxis _ANSI_ARGS_((Tcl_Interp *interp, char *str, int *valPtr));
171static int GetColor _ANSI_ARGS_((Tcl_Interp *interp, char *str, float *rgbPtr));
172
173/*
174 * ----------------------------------------------------------------------
175 * CLIENT COMMAND:
176 *   camera aim <x0> <y0> <z0>
177 *   camera angle <xAngle> <yAngle> <zAngle>
178 *   camera zoom <factor>
179 *
180 * Clients send these commands to manipulate the camera.  The "angle"
181 * operation controls the angle of the camera around the focal point.
182 * The "zoom" operation sets the zoom factor, moving the camera in
183 * and out.
184 * ----------------------------------------------------------------------
185 */
186static int
187CameraCmd(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
188{
189        if (argc < 2) {
190                Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
191                        " option arg arg...\"", (char*)NULL);
192                return TCL_ERROR;
193    }
194
195    char c = *argv[1];
196        if (c == 'a' && strcmp(argv[1],"angle") == 0) {
197        if (argc != 5) {
198                    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
199                            " angle xangle yangle zangle\"", (char*)NULL);
200                    return TCL_ERROR;
201        }
202
203        double xangle, yangle, zangle;
204            if (Tcl_GetDouble(interp, argv[2], &xangle) != TCL_OK) {
205                    return TCL_ERROR;
206            }
207            if (Tcl_GetDouble(interp, argv[3], &yangle) != TCL_OK) {
208                    return TCL_ERROR;
209            }
210            if (Tcl_GetDouble(interp, argv[4], &zangle) != TCL_OK) {
211                    return TCL_ERROR;
212            }
213            cam->rotate(xangle, yangle, zangle);
214
215            return TCL_OK;
216        }
217        else if (c == 'a' && strcmp(argv[1],"aim") == 0) {
218        if (argc != 5) {
219                    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
220                            " aim x y z\"", (char*)NULL);
221                    return TCL_ERROR;
222        }
223
224        double x0, y0, z0;
225            if (Tcl_GetDouble(interp, argv[2], &x0) != TCL_OK) {
226                    return TCL_ERROR;
227            }
228            if (Tcl_GetDouble(interp, argv[3], &y0) != TCL_OK) {
229                    return TCL_ERROR;
230            }
231            if (Tcl_GetDouble(interp, argv[4], &z0) != TCL_OK) {
232                    return TCL_ERROR;
233            }
234            cam->aim(x0, y0, z0);
235
236            return TCL_OK;
237        }
238        else if (c == 'z' && strcmp(argv[1],"zoom") == 0) {
239        if (argc != 3) {
240                    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
241                            " zoom factor\"", (char*)NULL);
242                    return TCL_ERROR;
243        }
244
245        double zoom;
246            if (Tcl_GetDouble(interp, argv[2], &zoom) != TCL_OK) {
247                    return TCL_ERROR;
248            }
249
250        live_obj_z = -2.5/zoom;
251                cam->move(live_obj_x, live_obj_y, live_obj_z);
252
253            return TCL_OK;
254    }
255
256        Tcl_AppendResult(interp, "bad option \"", argv[1],
257                "\": should be aim, angle, or zoom", (char*)NULL);
258        return TCL_ERROR;
259}
260
261/*
262 * ----------------------------------------------------------------------
263 * CLIENT COMMAND:
264 *   cutplane state on|off <axis> ?<volume>...?
265 *   cutplane position <relvalue> <axis> ?<volume>...?
266 *
267 * Clients send these commands to manipulate the cutplanes in one or
268 * more data volumes.  The "state" command turns a cutplane on or
269 * off.  The "position" command changes the position to a relative
270 * value in the range 0-1.  The <axis> can be x, y, or z.  These
271 * operations are applied to the volumes represented by one or more
272 * <volume> indices.  If no volumes are specified, then all volumes
273 * are updated.
274 * ----------------------------------------------------------------------
275 */
276static int
277CutplaneCmd(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
278{
279    if (argc < 2) {
280        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
281            " option ?arg arg...?\"", (char*)NULL);
282        return TCL_ERROR;
283    }
284
285    char c = *argv[1];
286    if (c == 's' && strcmp(argv[1],"state") == 0) {
287        if (argc < 4) {
288            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
289                " state on|off axis ?volume ...? \"", (char*)NULL);
290            return TCL_ERROR;
291        }
292
293        int state;
294        if (Tcl_GetBoolean(interp, argv[2], &state) != TCL_OK) {
295            return TCL_ERROR;
296        }
297
298        int axis;
299        if (GetAxis(interp, (char*) argv[3], &axis) != TCL_OK) {
300            return TCL_ERROR;
301        }
302
303        vector<int> ivol;
304        if (GetVolumeIndices(interp, argc-4, argv+4, &ivol) != TCL_OK) {
305            return TCL_ERROR;
306        }
307
308        vector<int>::iterator iter = ivol.begin();
309        while (iter != ivol.end()) {
310            if (state) {
311                volume[*iter]->enable_cutplane(axis);
312            } else {
313                volume[*iter]->disable_cutplane(axis);
314            }
315            ++iter;
316        }
317        return TCL_OK;
318    }
319    else if (c == 'p' && strcmp(argv[1],"position") == 0) {
320        if (argc < 4) {
321            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
322                " position relval axis ?volume ...? \"", (char*)NULL);
323            return TCL_ERROR;
324        }
325
326        double relval;
327        if (Tcl_GetDouble(interp, argv[2], &relval) != TCL_OK) {
328            return TCL_ERROR;
329        }
330        // keep this just inside the volume so it doesn't disappear
331        if (relval < 0.01) { relval = 0.01; }
332        if (relval > 0.99) { relval = 0.99; }
333
334        int axis;
335        if (GetAxis(interp, (char*) argv[3], &axis) != TCL_OK) {
336            return TCL_ERROR;
337        }
338
339        vector<int> ivol;
340        if (GetVolumeIndices(interp, argc-4, argv+4, &ivol) != TCL_OK) {
341            return TCL_ERROR;
342        }
343
344        vector<int>::iterator iter = ivol.begin();
345        while (iter != ivol.end()) {
346            volume[*iter]->move_cutplane(axis, (float)relval);
347            ++iter;
348        }
349        return TCL_OK;
350    }
351
352    Tcl_AppendResult(interp, "bad option \"", argv[1],
353        "\": should be position or state", (char*)NULL);
354    return TCL_ERROR;
355}
356
357/*
358 * ----------------------------------------------------------------------
359 * CLIENT COMMAND:
360 *   legend <volumeIndex> <width> <height>
361 *
362 * Clients use this to generate a legend image for the specified
363 * transfer function.  The legend image is a color gradient from 0
364 * to one, drawn in the given transfer function.  The resulting image
365 * is returned in the size <width> x <height>.
366 * ----------------------------------------------------------------------
367 */
368static int
369LegendCmd(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
370{
371    if (argc != 4) {
372        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
373            " transfunc width height\"", (char*)NULL);
374        return TCL_ERROR;
375    }
376
377    TransferFunction *tf = NULL;
378    int ivol;
379    if (Tcl_GetInt(interp, argv[1], &ivol) != TCL_OK) {
380        return TCL_ERROR;
381    }
382    if (ivol < n_volumes) {
383        tf = vol_render->get_volume_shading(volume[ivol]);
384    }
385    if (tf == NULL) {
386        Tcl_AppendResult(interp, "transfer function not defined for volume ", argv[1], (char*)NULL);
387        return TCL_ERROR;
388    }
389
390    int old_width = win_width;
391    int old_height = win_height;
392
393    int width, height;
394    if (Tcl_GetInt(interp, argv[2], &width) != TCL_OK) {
395        return TCL_ERROR;
396    }
397    if (Tcl_GetInt(interp, argv[3], &height) != TCL_OK) {
398        return TCL_ERROR;
399    }
400
401    plane_render->set_screen_size(width, height);
402    resize_offscreen_buffer(width, height);
403
404    // generate data for the legend
405    float data[512];
406    for (int i=0; i < 256; i++) {
407        data[i] = data[i+256] = (float)(i/255.0);
408    }
409    plane[0] = new Texture2D(256, 2, GL_FLOAT, GL_LINEAR, 1, data);
410    int index = plane_render->add_plane(plane[0], tf);
411    plane_render->set_active_plane(index);
412
413    offscreen_buffer_capture();
414    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear screen
415    plane_render->render();
416
417    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, screen_buffer);
418
419    std::ostringstream result;
420    result << "nv>legend " << argv[1];
421    result << " " << volume[ivol]->range_min();
422    result << " " << volume[ivol]->range_max();
423    bmp_write(result.str().c_str());
424    write(0, "\n", 1);
425
426    plane_render->remove_plane(index);
427    resize_offscreen_buffer(old_width, old_height);
428
429    return TCL_OK;
430}
431
432
433
434/*
435 * ----------------------------------------------------------------------
436 * CLIENT COMMAND:
437 *   screen <width> <height>
438 *
439 * Clients send this command to set the size of the rendering area.
440 * Future images are generated at the specified width/height.
441 * ----------------------------------------------------------------------
442 */
443static int
444ScreenCmd(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
445{
446    int w, h;
447
448    if (argc != 3) {
449        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
450            " width height\"", (char*)NULL);
451        return TCL_ERROR;
452    }
453    if (Tcl_GetInt(interp, argv[1], &w) != TCL_OK) {
454        return TCL_ERROR;
455    }
456    if (Tcl_GetInt(interp, argv[2], &h) != TCL_OK) {
457        return TCL_ERROR;
458    }
459    resize_offscreen_buffer(w, h);
460
461    return TCL_OK;
462}
463
464/*
465 * ----------------------------------------------------------------------
466 * CLIENT COMMAND:
467 *   transfunc define <name> <colormap> <alphamap>
468 *     where <colormap> = { <v> <r> <g> <b> ... }
469 *           <alphamap> = { <v> <w> ... }
470 *
471 * Clients send these commands to manipulate the transfer functions.
472 * ----------------------------------------------------------------------
473 */
474static int
475TransfuncCmd(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
476{
477        if (argc < 2) {
478                Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
479                        " option arg arg...\"", (char*)NULL);
480                return TCL_ERROR;
481    }
482
483    char c = *argv[1];
484        if (c == 'd' && strcmp(argv[1],"define") == 0) {
485        if (argc != 5) {
486                    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
487                            argv[1], " define name colormap alphamap\"", (char*)NULL);
488            return TCL_ERROR;
489        }
490
491        // decode the data and store in a series of fields
492        Rappture::Field1D rFunc, gFunc, bFunc, wFunc;
493        int cmapc, wmapc, i, j;
494        char **cmapv, **wmapv;
495
496        if (Tcl_SplitList(interp, argv[3], &cmapc, &cmapv) != TCL_OK) {
497            return TCL_ERROR;
498        }
499        if (cmapc % 4 != 0) {
500            Tcl_Free((char*)cmapv);
501                    Tcl_AppendResult(interp, "bad colormap in transfunc: should be ",
502                "{ v r g b ... }", (char*)NULL);
503            return TCL_ERROR;
504        }
505
506        if (Tcl_SplitList(interp, argv[4], &wmapc, &wmapv) != TCL_OK) {
507            return TCL_ERROR;
508        }
509        if (wmapc % 2 != 0) {
510            Tcl_Free((char*)cmapv);
511            Tcl_Free((char*)wmapv);
512                    Tcl_AppendResult(interp, "bad alphamap in transfunc: should be ",
513                "{ v w ... }", (char*)NULL);
514            return TCL_ERROR;
515        }
516
517        for (i=0; i < cmapc; i += 4) {
518            double vals[4];
519            for (j=0; j < 4; j++) {
520                if (Tcl_GetDouble(interp, cmapv[i+j], &vals[j]) != TCL_OK) {
521                    Tcl_Free((char*)cmapv);
522                    Tcl_Free((char*)wmapv);
523                    return TCL_ERROR;
524                }
525                if (vals[j] < 0 || vals[j] > 1) {
526                    Tcl_Free((char*)cmapv);
527                    Tcl_Free((char*)wmapv);
528                            Tcl_AppendResult(interp, "bad value \"", cmapv[i+j],
529                        "\": should be in the range 0-1", (char*)NULL);
530                    return TCL_ERROR;
531                }
532            }
533            rFunc.define(vals[0], vals[1]);
534            gFunc.define(vals[0], vals[2]);
535            bFunc.define(vals[0], vals[3]);
536        }
537
538        for (i=0; i < wmapc; i += 2) {
539            double vals[2];
540            for (j=0; j < 2; j++) {
541                if (Tcl_GetDouble(interp, wmapv[i+j], &vals[j]) != TCL_OK) {
542                    Tcl_Free((char*)cmapv);
543                    Tcl_Free((char*)wmapv);
544                    return TCL_ERROR;
545                }
546                if (vals[j] < 0 || vals[j] > 1) {
547                    Tcl_Free((char*)cmapv);
548                    Tcl_Free((char*)wmapv);
549                            Tcl_AppendResult(interp, "bad value \"", wmapv[i+j],
550                        "\": should be in the range 0-1", (char*)NULL);
551                    return TCL_ERROR;
552                }
553            }
554            wFunc.define(vals[0], vals[1]);
555        }
556        Tcl_Free((char*)cmapv);
557        Tcl_Free((char*)wmapv);
558
559        // sample the given function into discrete slots
560        const int nslots = 256;
561        float data[4*nslots];
562        for (i=0; i < nslots; i++) {
563            double xval = double(i)/(nslots-1);
564            data[4*i]   = rFunc.value(xval);
565            data[4*i+1] = gFunc.value(xval);
566            data[4*i+2] = bFunc.value(xval);
567            data[4*i+3] = wFunc.value(xval);
568        }
569
570        // find or create this transfer function
571        int newEntry;
572        Tcl_HashEntry *entryPtr;
573        TransferFunction *tf;
574
575        entryPtr = Tcl_CreateHashEntry(&tftable, argv[2], &newEntry);
576        if (newEntry) {
577            tf = new TransferFunction(nslots, data);
578            Tcl_SetHashValue(entryPtr, (ClientData)tf);
579        } else {
580            tf = (TransferFunction*)Tcl_GetHashValue(entryPtr);
581            tf->update(data);
582        }
583
584        return TCL_OK;
585    }
586    Tcl_AppendResult(interp, "bad option \"", argv[1],
587        "\": should be define", (char*)NULL);
588    return TCL_ERROR;
589}
590
591/*
592 * ----------------------------------------------------------------------
593 * CLIENT COMMAND:
594 *   up axis
595 *
596 * Clients use this to set the "up" direction for all volumes.  Volumes
597 * are oriented such that this direction points upward.
598 * ----------------------------------------------------------------------
599 */
600static int
601UpCmd(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
602{
603    if (argc != 2) {
604        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
605            " x|y|z|-x|-y|-z\"", (char*)NULL);
606        return TCL_ERROR;
607    }
608
609    int sign = 1;
610    char *axisName = argv[1];
611    if (*axisName == '-') {
612        sign = -1;
613        axisName++;
614    }
615
616    int axis;
617    if (GetAxis(interp, axisName, &axis) != TCL_OK) {
618        return TCL_ERROR;
619    }
620
621    updir = (axis+1)*sign;
622
623    return TCL_OK;
624}
625
626/*
627 * ----------------------------------------------------------------------
628 * CLIENT COMMAND:
629 *   volume axis label x|y|z <value> ?<volumeId> ...?
630 *   volume data state on|off ?<volumeId> ...?
631 *   volume outline state on|off ?<volumeId> ...?
632 *   volume outline color on|off ?<volumeId> ...?
633 *   volume shading transfunc <name> ?<volumeId> ...?
634 *   volume shading diffuse <value> ?<volumeId> ...?
635 *   volume shading specular <value> ?<volumeId> ...?
636 *   volume shading opacity <value> ?<volumeId> ...?
637 *   volume state on|off ?<volumeId> ...?
638 *
639 * Clients send these commands to manipulate the volumes.
640 * ----------------------------------------------------------------------
641 */
642static int
643VolumeCmd(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])
644{
645    if (argc < 2) {
646        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
647            " option arg arg...\"", (char*)NULL);
648        return TCL_ERROR;
649    }
650
651    char c = *argv[1];
652    if (c == 'a' && strcmp(argv[1],"axis") == 0) {
653        if (argc < 3) {
654            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
655                argv[1], " option ?arg arg...?\"", (char*)NULL);
656            return TCL_ERROR;
657        }
658        c = *argv[2];
659        if (c == 'l' && strcmp(argv[2],"label") == 0) {
660            if (argc < 4) {
661                Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
662                    argv[1], " label x|y|z string ?volume ...?\"", (char*)NULL);
663                return TCL_ERROR;
664            }
665
666            int axis;
667            if (GetAxis(interp, argv[3], &axis) != TCL_OK) {
668                return TCL_ERROR;
669            }
670
671            vector<int> ivol;
672            if (GetVolumeIndices(interp, argc-5, argv+5, &ivol) != TCL_OK) {
673                return TCL_ERROR;
674            }
675
676            vector<int>::iterator iter = ivol.begin();
677            while (iter != ivol.end()) {
678                volume[*iter]->set_label(axis, argv[4]);
679                ++iter;
680            }
681            return TCL_OK;
682        }
683        Tcl_AppendResult(interp, "bad option \"", argv[2],
684            "\": should be label", (char*)NULL);
685        return TCL_ERROR;
686    }
687    else if (c == 'd' && strcmp(argv[1],"data") == 0) {
688        if (argc < 3) {
689            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
690                argv[1], " option ?arg arg...?\"", (char*)NULL);
691            return TCL_ERROR;
692        }
693        c = *argv[2];
694        if (c == 's' && strcmp(argv[2],"state") == 0) {
695            if (argc < 4) {
696                Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
697                    argv[1], " state on|off ?volume ...?\"", (char*)NULL);
698                return TCL_ERROR;
699            }
700
701            int state;
702            if (Tcl_GetBoolean(interp, argv[3], &state) != TCL_OK) {
703                return TCL_ERROR;
704            }
705
706            vector<int> ivol;
707            if (GetVolumeIndices(interp, argc-4, argv+4, &ivol) != TCL_OK) {
708                return TCL_ERROR;
709            }
710
711            vector<int>::iterator iter = ivol.begin();
712            while (iter != ivol.end()) {
713                if (state) {
714                    volume[*iter]->enable_data();
715                } else {
716                    volume[*iter]->disable_data();
717                }
718                ++iter;
719            }
720            return TCL_OK;
721        }
722        else if (c == 'f' && strcmp(argv[2],"follows") == 0) {
723            int nbytes;
724            if (Tcl_GetInt(interp, argv[3], &nbytes) != TCL_OK) {
725                return TCL_ERROR;
726            }
727
728            char fname[64];
729            sprintf(fname,"/tmp/nv%d.dat",getpid());
730            std::ofstream dfile(fname);
731
732            char buffer[8096];
733            while (nbytes > 0) {
734                int chunk = (sizeof(buffer) < nbytes) ? sizeof(buffer) : nbytes;
735                int status = fread(buffer, 1, chunk, stdin);
736                if (status > 0) {
737                    dfile.write(buffer,status);
738                    nbytes -= status;
739                } else {
740                    Tcl_AppendResult(interp, "data unpacking failed in file ",
741                        fname, (char*)NULL);
742                    return TCL_ERROR;
743                }
744            }
745            dfile.close();
746
747            char cmdstr[512];
748            sprintf(cmdstr, "mimedecode %s | gunzip -c > /tmp/nv%d.dx", fname, getpid());
749            if (system(cmdstr) != 0) {
750                Tcl_AppendResult(interp, "data unpacking failed in file ",
751                    fname, (char*)NULL);
752                return TCL_ERROR;
753            }
754
755            sprintf(fname,"/tmp/nv%d.dx",getpid());
756
757            int n = n_volumes;
758            Rappture::Outcome err = load_volume_file(n, fname);
759
760            sprintf(cmdstr, "rm -f /tmp/nv%d.dat /tmp/nv%d.dx", getpid(), getpid());
761            system(cmdstr);
762
763            if (err) {
764                Tcl_AppendResult(interp, err.remark().c_str(), (char*)NULL);
765                return TCL_ERROR;
766            }
767
768            //
769            // BE CAREFUL:  Set the number of slices to something
770            //   slightly different for each volume.  If we have
771            //   identical volumes at exactly the same position
772            //   with exactly the same number of slices, the second
773            //   volume will overwrite the first, so the first won't
774            //   appear at all.
775            //
776            volume[n]->set_n_slice(256-n);
777            volume[n]->disable_cutplane(0);
778            volume[n]->disable_cutplane(1);
779            volume[n]->disable_cutplane(2);
780            vol_render->add_volume(volume[n], get_transfunc("default"));
781
782            return TCL_OK;
783        }
784        Tcl_AppendResult(interp, "bad option \"", argv[2],
785            "\": should be follows or state", (char*)NULL);
786        return TCL_ERROR;
787    }
788    else if (c == 'o' && strcmp(argv[1],"outline") == 0) {
789        if (argc < 3) {
790            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
791                argv[1], " option ?arg arg...?\"", (char*)NULL);
792            return TCL_ERROR;
793        }
794        c = *argv[2];
795        if (c == 's' && strcmp(argv[2],"state") == 0) {
796            if (argc < 3) {
797                Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
798                    argv[1], " state on|off ?volume ...? \"", (char*)NULL);
799                return TCL_ERROR;
800            }
801
802            int state;
803            if (Tcl_GetBoolean(interp, argv[3], &state) != TCL_OK) {
804                return TCL_ERROR;
805            }
806
807            vector<int> ivol;
808            if (GetVolumeIndices(interp, argc-4, argv+4, &ivol) != TCL_OK) {
809                return TCL_ERROR;
810            }
811
812            vector<int>::iterator iter = ivol.begin();
813            while (iter != ivol.end()) {
814                if (state) {
815                    volume[*iter]->enable_outline();
816                } else {
817                    volume[*iter]->disable_outline();
818                }
819                ++iter;
820            }
821            return TCL_OK;
822        }
823        else if (c == 'c' && strcmp(argv[2],"color") == 0) {
824            if (argc < 3) {
825                Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
826                    argv[1], " color {R G B} ?volume ...? \"", (char*)NULL);
827                return TCL_ERROR;
828            }
829
830            float rgb[3];
831            if (GetColor(interp, (char*) argv[3], rgb) != TCL_OK) {
832                return TCL_ERROR;
833            }
834
835            vector<int> ivol;
836            if (GetVolumeIndices(interp, argc-4, argv+4, &ivol) != TCL_OK) {
837                return TCL_ERROR;
838            }
839
840            vector<int>::iterator iter = ivol.begin();
841            while (iter != ivol.end()) {
842                volume[*iter]->set_outline_color(rgb);
843                ++iter;
844            }
845            return TCL_OK;
846        }
847
848        Tcl_AppendResult(interp, "bad option \"", argv[2],
849            "\": should be color or state", (char*)NULL);
850        return TCL_ERROR;
851    }
852    else if (c == 's' && strcmp(argv[1],"shading") == 0) {
853        if (argc < 3) {
854            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
855                argv[1], " option ?arg arg...?\"", (char*)NULL);
856            return TCL_ERROR;
857        }
858        c = *argv[2];
859        if (c == 't' && strcmp(argv[2],"transfunc") == 0) {
860            if (argc < 4) {
861                Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
862                    argv[1], " transfunc name ?volume ...?\"", (char*)NULL);
863                return TCL_ERROR;
864            }
865
866            TransferFunction *tf = get_transfunc(argv[3]);
867            if (tf == NULL) {
868                Tcl_AppendResult(interp, "transfer function \"", argv[3],
869                    "\" is not defined", (char*)NULL);
870                return TCL_ERROR;
871            }
872
873            vector<int> ivol;
874            if (GetVolumeIndices(interp, argc-4, argv+4, &ivol) != TCL_OK) {
875                return TCL_ERROR;
876            }
877
878            vector<int>::iterator iter = ivol.begin();
879            while (iter != ivol.end()) {
880                vol_render->shade_volume(volume[*iter], tf);
881                ++iter;
882            }
883            return TCL_OK;
884        }
885        else if (c == 'd' && strcmp(argv[2],"diffuse") == 0) {
886            if (argc < 4) {
887                Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
888                    argv[1], " diffuse value ?volume ...?\"", (char*)NULL);
889                return TCL_ERROR;
890            }
891
892            double dval;
893            if (Tcl_GetDouble(interp, argv[3], &dval) != TCL_OK) {
894                return TCL_ERROR;
895            }
896
897            vector<int> ivol;
898            if (GetVolumeIndices(interp, argc-4, argv+4, &ivol) != TCL_OK) {
899                return TCL_ERROR;
900            }
901
902            vector<int>::iterator iter = ivol.begin();
903            while (iter != ivol.end()) {
904                volume[*iter]->set_diffuse((float)dval);
905                ++iter;
906            }
907            return TCL_OK;
908        }
909        else if (c == 'o' && strcmp(argv[2],"opacity") == 0) {
910            if (argc < 4) {
911                Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
912                    argv[1], " opacity value ?volume ...?\"", (char*)NULL);
913                return TCL_ERROR;
914            }
915
916            double dval;
917            if (Tcl_GetDouble(interp, argv[3], &dval) != TCL_OK) {
918                return TCL_ERROR;
919            }
920
921            vector<int> ivol;
922            if (GetVolumeIndices(interp, argc-4, argv+4, &ivol) != TCL_OK) {
923                return TCL_ERROR;
924            }
925
926            vector<int>::iterator iter = ivol.begin();
927            while (iter != ivol.end()) {
928                volume[*iter]->set_opacity_scale((float)dval);
929                ++iter;
930            }
931            return TCL_OK;
932        }
933        else if (c == 's' && strcmp(argv[2],"specular") == 0) {
934            if (argc < 4) {
935                Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
936                    argv[1], " specular value ?volume ...?\"", (char*)NULL);
937                return TCL_ERROR;
938            }
939
940            double dval;
941            if (Tcl_GetDouble(interp, argv[3], &dval) != TCL_OK) {
942                return TCL_ERROR;
943            }
944
945            vector<int> ivol;
946            if (GetVolumeIndices(interp, argc-4, argv+4, &ivol) != TCL_OK) {
947                return TCL_ERROR;
948            }
949
950            vector<int>::iterator iter = ivol.begin();
951            while (iter != ivol.end()) {
952                volume[*iter]->set_specular((float)dval);
953                ++iter;
954            }
955            return TCL_OK;
956        }
957        Tcl_AppendResult(interp, "bad option \"", argv[2],
958            "\": should be diffuse, opacity, specular, or transfunc", (char*)NULL);
959        return TCL_ERROR;
960    }
961    else if (c == 's' && strcmp(argv[1],"state") == 0) {
962        if (argc < 3) {
963            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
964                argv[1], " on|off ?volume...?\"", (char*)NULL);
965            return TCL_ERROR;
966        }
967
968        int state;
969        if (Tcl_GetBoolean(interp, argv[2], &state) != TCL_OK) {
970            return TCL_ERROR;
971        }
972
973        vector<int> ivol;
974        if (GetVolumeIndices(interp, argc-3, argv+3, &ivol) != TCL_OK) {
975            return TCL_ERROR;
976        }
977
978        vector<int>::iterator iter = ivol.begin();
979        while (iter != ivol.end()) {
980            if (state) {
981                volume[*iter]->enable();
982            } else {
983                volume[*iter]->disable();
984            }
985            ++iter;
986        }
987        return TCL_OK;
988    }
989
990    Tcl_AppendResult(interp, "bad option \"", argv[1],
991        "\": should be data, outline, shading, or state", (char*)NULL);
992    return TCL_ERROR;
993}
994
995/*
996 * ----------------------------------------------------------------------
997 * FUNCTION: GetVolumeIndices()
998 *
999 * Used internally to decode a series of volume index values and
1000 * store then in the specified vector.  If there are no volume index
1001 * arguments, this means "all volumes" to most commands, so all
1002 * active volume indices are stored in the vector.
1003 *
1004 * Updates pushes index values into the vector.  Returns TCL_OK or
1005 * TCL_ERROR to indicate an error.
1006 * ----------------------------------------------------------------------
1007 */
1008static int
1009GetVolumeIndices(Tcl_Interp *interp, int argc, CONST84 char *argv[],
1010    vector<int>* vectorPtr)
1011{
1012    if (argc == 0) {
1013        for (int n=0; n < volume.size(); n++) {
1014            if (volume[n] != NULL) {
1015                vectorPtr->push_back(n);
1016            }
1017        }
1018    } else {
1019        int ivol;
1020        for (int n=0; n < argc; n++) {
1021            if (Tcl_GetInt(interp, argv[n], &ivol) != TCL_OK) {
1022                return TCL_ERROR;
1023            }
1024            if (ivol < 0 || ivol >= volume.size()) {
1025                Tcl_AppendResult(interp, "bad volume index \"", argv[n],
1026                    "\"", (char*)NULL);
1027                return TCL_ERROR;
1028            }
1029            if (volume[ivol] != NULL) {
1030                vectorPtr->push_back(ivol);
1031            }
1032        }
1033    }
1034    return TCL_OK;
1035}
1036
1037/*
1038 * ----------------------------------------------------------------------
1039 * FUNCTION: GetAxis()
1040 *
1041 * Used internally to decode an axis value from a string ("x", "y",
1042 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
1043 * along with a value in valPtr.  Otherwise, it returns TCL_ERROR
1044 * and an error message in the interpreter.
1045 * ----------------------------------------------------------------------
1046 */
1047static int
1048GetAxis(Tcl_Interp *interp, char *str, int *valPtr)
1049{
1050    if (strcmp(str,"x") == 0) {
1051        *valPtr = 0;
1052        return TCL_OK;
1053    }
1054    else if (strcmp(str,"y") == 0) {
1055        *valPtr = 1;
1056        return TCL_OK;
1057    }
1058    else if (strcmp(str,"z") == 0) {
1059        *valPtr = 2;
1060        return TCL_OK;
1061    }
1062    Tcl_AppendResult(interp, "bad axis \"", str,
1063        "\": should be x, y, or z", (char*)NULL);
1064    return TCL_ERROR;
1065}
1066
1067/*
1068 * ----------------------------------------------------------------------
1069 * FUNCTION: GetColor()
1070 *
1071 * Used internally to decode a color value from a string ("R G B")
1072 * as a list of three numbers 0-1.  Returns TCL_OK if successful,
1073 * along with RGB values in valPtr.  Otherwise, it returns TCL_ERROR
1074 * and an error message in the interpreter.
1075 * ----------------------------------------------------------------------
1076 */
1077static int
1078GetColor(Tcl_Interp *interp, char *str, float *rgbPtr)
1079{
1080    int rgbc;
1081    char **rgbv;
1082    if (Tcl_SplitList(interp, str, &rgbc, &rgbv) != TCL_OK) {
1083        return TCL_ERROR;
1084    }
1085    if (rgbc != 3) {
1086        Tcl_AppendResult(interp, "bad color \"", str,
1087            "\": should be {R G B} as double values 0-1", (char*)NULL);
1088        return TCL_ERROR;
1089    }
1090
1091    double rval, gval, bval;
1092    if (Tcl_GetDouble(interp, rgbv[0], &rval) != TCL_OK) {
1093        Tcl_Free((char*)rgbv);
1094        return TCL_ERROR;
1095    }
1096    if (Tcl_GetDouble(interp, rgbv[1], &gval) != TCL_OK) {
1097        Tcl_Free((char*)rgbv);
1098        return TCL_ERROR;
1099    }
1100    if (Tcl_GetDouble(interp, rgbv[2], &bval) != TCL_OK) {
1101        Tcl_Free((char*)rgbv);
1102        return TCL_ERROR;
1103    }
1104    Tcl_Free((char*)rgbv);
1105
1106    rgbPtr[0] = (float)rval;
1107    rgbPtr[1] = (float)gval;
1108    rgbPtr[2] = (float)bval;
1109
1110    return TCL_OK;
1111}
1112
1113/*
1114 * ----------------------------------------------------------------------
1115 * USAGE: debug("string", ...)
1116 *
1117 * Use this anywhere within the package to send a debug message
1118 * back to the client.  The string can have % fields as used by
1119 * the printf() package.  Any remaining arguments are treated as
1120 * field substitutions on that.
1121 * ----------------------------------------------------------------------
1122 */
1123void
1124debug(char *str)
1125{
1126    write(0, str, strlen(str));
1127}
1128
1129void
1130debug(char *str, double v1)
1131{
1132    char buffer[512];
1133    sprintf(buffer, str, v1);
1134    write(0, buffer, strlen(buffer));
1135}
1136
1137void
1138debug(char *str, double v1, double v2)
1139{
1140    char buffer[512];
1141    sprintf(buffer, str, v1, v2);
1142    write(0, buffer, strlen(buffer));
1143}
1144
1145void
1146debug(char *str, double v1, double v2, double v3)
1147{
1148    char buffer[512];
1149    sprintf(buffer, str, v1, v2, v3);
1150    write(0, buffer, strlen(buffer));
1151}
1152
1153
1154static int
1155PlaneNewCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])){
1156  fprintf(stderr, "load plane for 2D visualization command\n");
1157
1158  int index, w, h;
1159
1160  if (argc != 4) {
1161    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1162                " plane_index w h \"", (char*)NULL);
1163    return TCL_ERROR;
1164  }
1165  if (Tcl_GetInt(interp, argv[1], &index) != TCL_OK) {
1166        return TCL_ERROR;
1167  }
1168  if (Tcl_GetInt(interp, argv[2], &w) != TCL_OK) {
1169        return TCL_ERROR;
1170  }
1171  if (Tcl_GetInt(interp, argv[3], &h) != TCL_OK) {
1172        return TCL_ERROR;
1173  }
1174
1175  //Now read w*h*4 bytes. The server expects the plane to be a stream of floats
1176  char* tmp = new char[int(w*h*sizeof(float))];
1177  bzero(tmp, w*h*4);
1178  int status = read(0, tmp, w*h*sizeof(float));
1179  if (status <= 0){
1180    exit(0);
1181  }
1182 
1183  plane[index] = new Texture2D(w, h, GL_FLOAT, GL_LINEAR, 1, (float*)tmp);
1184 
1185  delete[] tmp;
1186  return TCL_OK;
1187}
1188
1189
1190static
1191int PlaneLinkCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])){
1192  fprintf(stderr, "link the plane to the 2D renderer command\n");
1193
1194  int plane_index, tf_index;
1195
1196  if (argc != 3) {
1197    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1198                " plane_index tf_index \"", (char*)NULL);
1199    return TCL_ERROR;
1200  }
1201  if (Tcl_GetInt(interp, argv[1], &plane_index) != TCL_OK) {
1202        return TCL_ERROR;
1203  }
1204  if (Tcl_GetInt(interp, argv[2], &tf_index) != TCL_OK) {
1205        return TCL_ERROR;
1206  }
1207
1208  //plane_render->add_plane(plane[plane_index], tf[tf_index]);
1209
1210  return TCL_OK;
1211}
1212
1213
1214//Enable a 2D plane for render
1215//The plane_index is the index mantained in the 2D plane renderer
1216static
1217int PlaneEnableCmd _ANSI_ARGS_((ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[])){
1218  fprintf(stderr, "enable a plane so the 2D renderer can render it command\n");
1219
1220  int plane_index, mode;
1221
1222  if (argc != 3) {
1223    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1224                " plane_index mode \"", (char*)NULL);
1225    return TCL_ERROR;
1226  }
1227  if (Tcl_GetInt(interp, argv[1], &plane_index) != TCL_OK) {
1228        return TCL_ERROR;
1229  }
1230  if (Tcl_GetInt(interp, argv[2], &mode) != TCL_OK) {
1231        return TCL_ERROR;
1232  }
1233
1234  if(mode==0)
1235    plane_render->set_active_plane(-1);
1236  else
1237    plane_render->set_active_plane(plane_index);
1238
1239  return TCL_OK;
1240}
1241
1242
1243//report errors related to CG shaders
1244void cgErrorCallback(void)
1245{
1246    CGerror lastError = cgGetError();
1247    if(lastError) {
1248        const char *listing = cgGetLastListing(g_context);
1249        printf("\n---------------------------------------------------\n");
1250        printf("%s\n\n", cgGetErrorString(lastError));
1251        printf("%s\n", listing);
1252        printf("-----------------------------------------------------\n");
1253        printf("Cg error, exiting...\n");
1254        cgDestroyContext(g_context);
1255        exit(-1);
1256    }
1257}
1258
1259
1260void init_glew(){
1261        GLenum err = glewInit();
1262        if (GLEW_OK != err)
1263        {
1264                //glew init failed, exit.
1265                fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
1266                getchar();
1267                assert(false);
1268        }
1269
1270        fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
1271}
1272
1273
1274
1275/* Load a 3D vector field from a dx-format file
1276 */
1277void
1278load_vector_file(int index, char *fname) {
1279    int dummy, nx, ny, nz, nxy, npts;
1280    double x0, y0, z0, dx, dy, dz, ddx, ddy, ddz;
1281    char line[128], type[128], *start;
1282    std::ifstream fin(fname);
1283
1284    do {
1285        fin.getline(line,sizeof(line)-1);
1286        for (start=&line[0]; *start == ' ' || *start == '\t'; start++)
1287            ;  // skip leading blanks
1288
1289        if (*start != '#') {  // skip comment lines
1290            if (sscanf(start, "object %d class gridpositions counts %d %d %d", &dummy, &nx, &ny, &nz) == 4) {
1291                // found grid size
1292            }
1293            else if (sscanf(start, "origin %lg %lg %lg", &x0, &y0, &z0) == 3) {
1294                // found origin
1295            }
1296            else if (sscanf(start, "delta %lg %lg %lg", &ddx, &ddy, &ddz) == 3) {
1297                // found one of the delta lines
1298                if (ddx != 0.0) { dx = ddx; }
1299                else if (ddy != 0.0) { dy = ddy; }
1300                else if (ddz != 0.0) { dz = ddz; }
1301            }
1302            else if (sscanf(start, "object %d class array type %s shape 3 rank 1 items %d data follows", &dummy, type, &npts) == 3) {
1303                if (npts != nx*ny*nz) {
1304                    std::cerr << "inconsistent data: expected " << nx*ny*nz << " points but found " << npts << " points" << std::endl;
1305                    return;
1306                }
1307                break;
1308            }
1309            else if (sscanf(start, "object %d class array type %s rank 0 times %d data follows", &dummy, type, &npts) == 3) {
1310                if (npts != nx*ny*nz) {
1311                    std::cerr << "inconsistent data: expected " << nx*ny*nz << " points but found " << npts << " points" << std::endl;
1312                    return;
1313                }
1314                break;
1315            }
1316        }
1317    } while (!fin.eof());
1318
1319    // read data points
1320    if (!fin.eof()) {
1321        Rappture::Mesh1D xgrid(x0, x0+nx*dx, nx);
1322        Rappture::Mesh1D ygrid(y0, y0+ny*dy, ny);
1323        Rappture::Mesh1D zgrid(z0, z0+nz*dz, nz);
1324        Rappture::FieldRect3D xfield(xgrid, ygrid, zgrid);
1325        Rappture::FieldRect3D yfield(xgrid, ygrid, zgrid);
1326        Rappture::FieldRect3D zfield(xgrid, ygrid, zgrid);
1327
1328        double vx, vy, vz;
1329        int nread = 0;
1330        for (int ix=0; ix < nx; ix++) {
1331            for (int iy=0; iy < ny; iy++) {
1332                for (int iz=0; iz < nz; iz++) {
1333                    if (fin.eof() || nread > npts) {
1334                        break;
1335                    }
1336                    fin.getline(line,sizeof(line)-1);
1337                    if (sscanf(line, "%lg %lg %lg", &vx, &vy, &vz) == 3) {
1338                        int nindex = iz*nx*ny + iy*nx + ix;
1339                        xfield.define(nindex, vx);
1340                        yfield.define(nindex, vy);
1341                        zfield.define(nindex, vz);
1342                        nread++;
1343                    }
1344                }
1345            }
1346        }
1347
1348        // make sure that we read all of the expected points
1349        if (nread != nx*ny*nz) {
1350            std::cerr << "inconsistent data: expected " << nx*ny*nz << " points but found " << nread << " points" << std::endl;
1351            return;
1352        }
1353
1354        // figure out a good mesh spacing
1355        int nsample = 30;
1356        dx = xfield.rangeMax(Rappture::xaxis) - xfield.rangeMin(Rappture::xaxis);
1357        dy = xfield.rangeMax(Rappture::yaxis) - xfield.rangeMin(Rappture::yaxis);
1358        dz = xfield.rangeMax(Rappture::zaxis) - xfield.rangeMin(Rappture::zaxis);
1359        double dmin = pow((dx*dy*dz)/(nsample*nsample*nsample), 0.333);
1360
1361        nx = (int)ceil(dx/dmin);
1362        ny = (int)ceil(dy/dmin);
1363        nz = (int)ceil(dz/dmin);
1364
1365#ifndef NV40
1366        // must be an even power of 2 for older cards
1367            nx = (int)pow(2.0, ceil(log10((double)nx)/log10(2.0)));
1368            ny = (int)pow(2.0, ceil(log10((double)ny)/log10(2.0)));
1369            nz = (int)pow(2.0, ceil(log10((double)nz)/log10(2.0)));
1370#endif
1371
1372        float *data = new float[3*nx*ny*nz];
1373
1374        std::cout << "generating " << nx << "x" << ny << "x" << nz << " = " << nx*ny*nz << " points" << std::endl;
1375
1376        // generate the uniformly sampled data that we need for a volume
1377        double vmin = 0.0;
1378        double vmax = 0.0;
1379        int ngen = 0;
1380        for (int iz=0; iz < nz; iz++) {
1381            double zval = z0 + iz*dmin;
1382            for (int iy=0; iy < ny; iy++) {
1383                double yval = y0 + iy*dmin;
1384                for (int ix=0; ix < nx; ix++) {
1385                    double xval = x0 + ix*dmin;
1386
1387                    vx = xfield.value(xval,yval,zval);
1388                    vy = yfield.value(xval,yval,zval);
1389                    vz = zfield.value(xval,yval,zval);
1390                    //vx = 1;
1391                    //vy = 1;
1392                    vz = 0;
1393                    double vm = sqrt(vx*vx + vy*vy + vz*vz);
1394
1395                    if (vm < vmin) { vmin = vm; }
1396                    if (vm > vmax) { vmax = vm; }
1397
1398                    data[ngen++] = vx;
1399                    data[ngen++] = vy;
1400                    data[ngen++] = vz;
1401                }
1402            }
1403        }
1404
1405        ngen = 0;
1406        for (ngen=0; ngen < npts; ngen++) {
1407            data[ngen] = (data[ngen]/(2.0*vmax) + 0.5);
1408        }
1409
1410        load_volume(index, nx, ny, nz, 3, data, vmin, vmax);
1411        delete [] data;
1412    } else {
1413        std::cerr << "WARNING: data not found in file " << fname << std::endl;
1414    }
1415}
1416
1417
1418/* Load a 3D volume from a dx-format file
1419 */
1420Rappture::Outcome
1421load_volume_file(int index, char *fname) {
1422    Rappture::Outcome result;
1423
1424    Rappture::MeshTri2D xymesh;
1425    int dummy, nx, ny, nz, nxy, npts;
1426    double x0, y0, z0, dx, dy, dz, ddx, ddy, ddz;
1427    char line[128], type[128], *start;
1428    std::ifstream fin(fname);
1429
1430    int isrect = 1;
1431
1432    do {
1433        fin.getline(line,sizeof(line)-1);
1434        for (start=&line[0]; *start == ' ' || *start == '\t'; start++)
1435            ;  // skip leading blanks
1436
1437        if (*start != '#') {  // skip comment lines
1438            if (sscanf(start, "object %d class gridpositions counts %d %d %d", &dummy, &nx, &ny, &nz) == 4) {
1439                // found grid size
1440                isrect = 1;
1441            }
1442            else if (sscanf(start, "object %d class array type float rank 1 shape 3 items %d data follows", &dummy, &nxy) == 2) {
1443                isrect = 0;
1444                double xx, yy, zz;
1445                for (int i=0; i < nxy; i++) {
1446                    fin.getline(line,sizeof(line)-1);
1447                    if (sscanf(line, "%lg %lg %lg", &xx, &yy, &zz) == 3) {
1448                        xymesh.addNode( Rappture::Node2D(xx,yy) );
1449                    }
1450                }
1451
1452                char fpts[128];
1453                sprintf(fpts, "/tmp/tmppts%d", getpid());
1454                char fcells[128];
1455                sprintf(fcells, "/tmp/tmpcells%d", getpid());
1456
1457                std::ofstream ftmp(fpts);
1458                // save corners of bounding box first, to work around meshing
1459                // problems in voronoi utility
1460                ftmp << xymesh.rangeMin(Rappture::xaxis) << " "
1461                     << xymesh.rangeMin(Rappture::yaxis) << std::endl;
1462                ftmp << xymesh.rangeMax(Rappture::xaxis) << " "
1463                     << xymesh.rangeMin(Rappture::yaxis) << std::endl;
1464                ftmp << xymesh.rangeMax(Rappture::xaxis) << " "
1465                     << xymesh.rangeMax(Rappture::yaxis) << std::endl;
1466                ftmp << xymesh.rangeMin(Rappture::xaxis) << " "
1467                     << xymesh.rangeMax(Rappture::yaxis) << std::endl;
1468                for (int i=0; i < nxy; i++) {
1469                    ftmp << xymesh.atNode(i).x() << " " << xymesh.atNode(i).y() << std::endl;
1470                }
1471                ftmp.close();
1472
1473                char cmdstr[512];
1474                sprintf(cmdstr, "voronoi -t < %s > %s", fpts, fcells);
1475                if (system(cmdstr) == 0) {
1476                    int cx, cy, cz;
1477                    std::ifstream ftri(fcells);
1478                    while (!ftri.eof()) {
1479                        ftri.getline(line,sizeof(line)-1);
1480                        if (sscanf(line, "%d %d %d", &cx, &cy, &cz) == 3) {
1481                            if (cx >= 4 && cy >= 4 && cz >= 4) {
1482                                // skip first 4 boundary points
1483                                xymesh.addCell(cx-4, cy-4, cz-4);
1484                            }
1485                        }
1486                    }
1487                    ftri.close();
1488                } else {
1489                    return result.error("triangularization failed");
1490                }
1491
1492                sprintf(cmdstr, "rm -f %s %s", fpts, fcells);
1493                system(cmdstr);
1494            }
1495            else if (sscanf(start, "object %d class regulararray count %d", &dummy, &nz) == 2) {
1496                // found z-grid
1497            }
1498            else if (sscanf(start, "origin %lg %lg %lg", &x0, &y0, &z0) == 3) {
1499                // found origin
1500            }
1501            else if (sscanf(start, "delta %lg %lg %lg", &ddx, &ddy, &ddz) == 3) {
1502                // found one of the delta lines
1503                if (ddx != 0.0) { dx = ddx; }
1504                else if (ddy != 0.0) { dy = ddy; }
1505                else if (ddz != 0.0) { dz = ddz; }
1506            }
1507            else if (sscanf(start, "object %d class array type %s rank 0 items %d data follows", &dummy, type, &npts) == 3) {
1508                if (isrect && (npts != nx*ny*nz)) {
1509                    char mesg[256];
1510                    sprintf(mesg,"inconsistent data: expected %d points but found %d points", nx*ny*nz, npts);
1511                    return result.error(mesg);
1512                }
1513                else if (!isrect && (npts != nxy*nz)) {
1514                    char mesg[256];
1515                    sprintf(mesg,"inconsistent data: expected %d points but found %d points", nxy*nz, npts);
1516                    return result.error(mesg);
1517                }
1518                break;
1519            }
1520            else if (sscanf(start, "object %d class array type %s rank 0 times %d data follows", &dummy, type, &npts) == 3) {
1521                if (npts != nx*ny*nz) {
1522                    char mesg[256];
1523                    sprintf(mesg,"inconsistent data: expected %d points but found %d points", nx*ny*nz, npts);
1524                    return result.error(mesg);
1525                }
1526                break;
1527            }
1528        }
1529    } while (!fin.eof());
1530
1531    // read data points
1532    if (!fin.eof()) {
1533        if (isrect) {
1534            Rappture::Mesh1D xgrid(x0, x0+nx*dx, nx);
1535            Rappture::Mesh1D ygrid(y0, y0+ny*dy, ny);
1536            Rappture::Mesh1D zgrid(z0, z0+nz*dz, nz);
1537            Rappture::FieldRect3D field(xgrid, ygrid, zgrid);
1538
1539            double dval;
1540            int nread = 0;
1541            int ix = 0;
1542            int iy = 0;
1543            int iz = 0;
1544            while (!fin.eof() && nread < npts) {
1545                fin.getline(line,sizeof(line)-1);
1546                if (sscanf(line, "%lg", &dval) == 1) {
1547                    int nindex = iz*nx*ny + iy*nx + ix;
1548                    field.define(nindex, dval);
1549                    nread++;
1550                    if (++iz >= nz) {
1551                        iz = 0;
1552                        if (++iy >= ny) {
1553                            iy = 0;
1554                            ++ix;
1555                        }
1556                    }
1557                }
1558            }
1559
1560            // make sure that we read all of the expected points
1561            if (nread != nx*ny*nz) {
1562                char mesg[256];
1563                sprintf(mesg,"inconsistent data: expected %d points but found %d points", nx*ny*nz, nread);
1564                result.error(mesg);
1565                return result;
1566            }
1567
1568            // figure out a good mesh spacing
1569            int nsample = 30;
1570            dx = field.rangeMax(Rappture::xaxis) - field.rangeMin(Rappture::xaxis);
1571            dy = field.rangeMax(Rappture::yaxis) - field.rangeMin(Rappture::yaxis);
1572            dz = field.rangeMax(Rappture::zaxis) - field.rangeMin(Rappture::zaxis);
1573            double dmin = pow((dx*dy*dz)/(nsample*nsample*nsample), 0.333);
1574
1575            nx = (int)ceil(dx/dmin);
1576            ny = (int)ceil(dy/dmin);
1577            nz = (int)ceil(dz/dmin);
1578
1579#ifndef NV40
1580            // must be an even power of 2 for older cards
1581                nx = (int)pow(2.0, ceil(log10((double)nx)/log10(2.0)));
1582                ny = (int)pow(2.0, ceil(log10((double)ny)/log10(2.0)));
1583                nz = (int)pow(2.0, ceil(log10((double)nz)/log10(2.0)));
1584#endif
1585
1586            float *data = new float[4*nx*ny*nz];
1587
1588            double vmin = field.valueMin();
1589            double dv = field.valueMax() - field.valueMin();
1590            if (dv == 0.0) { dv = 1.0; }
1591
1592            // generate the uniformly sampled data that we need for a volume
1593            int ngen = 0;
1594            for (int iz=0; iz < nz; iz++) {
1595                double zval = z0 + iz*dmin;
1596                for (int iy=0; iy < ny; iy++) {
1597                    double yval = y0 + iy*dmin;
1598                    for (int ix=0; ix < nx; ix++) {
1599                        double xval = x0 + ix*dmin;
1600                        double v = field.value(xval,yval,zval);
1601
1602                        // scale all values [0-1], -1 => out of bounds
1603                        v = (isnan(v)) ? -1.0 : (v - vmin)/dv;
1604                        data[ngen] = v;
1605                        ngen += 4;
1606                    }
1607                }
1608            }
1609
1610            // Compute the gradient of this data.  BE CAREFUL: center
1611            // calculation on each node to avoid skew in either direction.
1612            ngen = 0;
1613            for (int iz=0; iz < nz; iz++) {
1614                for (int iy=0; iy < ny; iy++) {
1615                    for (int ix=0; ix < nx; ix++) {
1616                        // gradient in x-direction
1617                        double valm1 = (ix == 0) ? 0.0 : data[ngen-1];
1618                        double valp1 = (ix == nx-1) ? 0.0 : data[ngen+1];
1619                        if (valm1 < 0 || valp1 < 0) {
1620                            data[ngen+1] = 0.0;
1621                        } else {
1622                            data[ngen+1] = valp1-valm1; // assume dx=1
1623                        }
1624
1625                        // gradient in y-direction
1626                        valm1 = (iy == 0) ? 0.0 : data[ngen-4*nx];
1627                        valp1 = (iy == ny-1) ? 0.0 : data[ngen+4*nx];
1628                        if (valm1 < 0 || valp1 < 0) {
1629                            data[ngen+2] = 0.0;
1630                        } else {
1631                            data[ngen+2] = valp1-valm1; // assume dy=1
1632                        }
1633
1634                        // gradient in z-direction
1635                        valm1 = (iz == 0) ? 0.0 : data[ngen-4*nx*ny];
1636                        valp1 = (iz == nz-1) ? 0.0 : data[ngen+4*nx*ny];
1637                        if (valm1 < 0 || valp1 < 0) {
1638                            data[ngen+3] = 0.0;
1639                        } else {
1640                            data[ngen+3] = valp1-valm1; // assume dz=1
1641                        }
1642
1643                        ngen += 4;
1644                    }
1645                }
1646            }
1647
1648            load_volume(index, nx, ny, nz, 4, data,
1649                field.valueMin(), field.valueMax());
1650
1651            delete [] data;
1652
1653        } else {
1654            Rappture::Mesh1D zgrid(z0, z0+nz*dz, nz);
1655            Rappture::FieldPrism3D field(xymesh, zgrid);
1656
1657            double dval;
1658            int nread = 0;
1659            int ixy = 0;
1660            int iz = 0;
1661            while (!fin.eof() && nread < npts) {
1662                if (!(fin >> dval).fail()) {
1663                    int nid = nxy*iz + ixy;
1664                    field.define(nid, dval);
1665
1666                    nread++;
1667                    if (++iz >= nz) {
1668                        iz = 0;
1669                        ixy++;
1670                    }
1671                }
1672            }
1673
1674            // make sure that we read all of the expected points
1675            if (nread != nxy*nz) {
1676                char mesg[256];
1677                sprintf(mesg,"inconsistent data: expected %d points but found %d points", nxy*nz, nread);
1678                return result.error(mesg);
1679            }
1680
1681            // figure out a good mesh spacing
1682            int nsample = 30;
1683            x0 = field.rangeMin(Rappture::xaxis);
1684            dx = field.rangeMax(Rappture::xaxis) - field.rangeMin(Rappture::xaxis);
1685            y0 = field.rangeMin(Rappture::yaxis);
1686            dy = field.rangeMax(Rappture::yaxis) - field.rangeMin(Rappture::yaxis);
1687            z0 = field.rangeMin(Rappture::zaxis);
1688            dz = field.rangeMax(Rappture::zaxis) - field.rangeMin(Rappture::zaxis);
1689            double dmin = pow((dx*dy*dz)/(nsample*nsample*nsample), 0.333);
1690
1691            nx = (int)ceil(dx/dmin);
1692            ny = (int)ceil(dy/dmin);
1693            nz = (int)ceil(dz/dmin);
1694#ifndef NV40
1695            // must be an even power of 2 for older cards
1696                nx = (int)pow(2.0, ceil(log10((double)nx)/log10(2.0)));
1697                ny = (int)pow(2.0, ceil(log10((double)ny)/log10(2.0)));
1698                nz = (int)pow(2.0, ceil(log10((double)nz)/log10(2.0)));
1699#endif
1700            float *data = new float[4*nx*ny*nz];
1701
1702            double vmin = field.valueMin();
1703            double dv = field.valueMax() - field.valueMin();
1704            if (dv == 0.0) { dv = 1.0; }
1705
1706            // generate the uniformly sampled data that we need for a volume
1707            int ngen = 0;
1708            for (iz=0; iz < nz; iz++) {
1709                double zval = z0 + iz*dmin;
1710                for (int iy=0; iy < ny; iy++) {
1711                    double yval = y0 + iy*dmin;
1712                    for (int ix=0; ix < nx; ix++) {
1713                        double xval = x0 + ix*dmin;
1714                        double v = field.value(xval,yval,zval);
1715
1716                        // scale all values [0-1], -1 => out of bounds
1717                        v = (isnan(v)) ? -1.0 : (v - vmin)/dv;
1718                        data[ngen] = v;
1719
1720                        ngen += 4;
1721                    }
1722                }
1723            }
1724
1725            // Compute the gradient of this data.  BE CAREFUL: center
1726            // calculation on each node to avoid skew in either direction.
1727            ngen = 0;
1728            for (int iz=0; iz < nz; iz++) {
1729                for (int iy=0; iy < ny; iy++) {
1730                    for (int ix=0; ix < nx; ix++) {
1731                        // gradient in x-direction
1732                        double valm1 = (ix == 0) ? 0.0 : data[ngen-1];
1733                        double valp1 = (ix == nx-1) ? 0.0 : data[ngen+1];
1734                        if (valm1 < 0 || valp1 < 0) {
1735                            data[ngen+1] = 0.0;
1736                        } else {
1737                            data[ngen+1] = valp1-valm1; // assume dx=1
1738                        }
1739
1740                        // gradient in y-direction
1741                        valm1 = (iy == 0) ? 0.0 : data[ngen-4*nx];
1742                        valp1 = (iy == ny-1) ? 0.0 : data[ngen+4*nx];
1743                        if (valm1 < 0 || valp1 < 0) {
1744                            data[ngen+2] = 0.0;
1745                        } else {
1746                            data[ngen+2] = valp1-valm1; // assume dy=1
1747                        }
1748
1749                        // gradient in z-direction
1750                        valm1 = (iz == 0) ? 0.0 : data[ngen-4*nx*ny];
1751                        valp1 = (iz == nz-1) ? 0.0 : data[ngen+4*nx*ny];
1752                        if (valm1 < 0 || valp1 < 0) {
1753                            data[ngen+3] = 0.0;
1754                        } else {
1755                            data[ngen+3] = valp1-valm1; // assume dz=1
1756                        }
1757
1758                        ngen += 4;
1759                    }
1760                }
1761            }
1762
1763            load_volume(index, nx, ny, nz, 4, data,
1764                field.valueMin(), field.valueMax());
1765
1766            delete [] data;
1767        }
1768    } else {
1769        char mesg[256];
1770        sprintf(mesg,"data not found in file %s", fname);
1771        return result.error(mesg);
1772    }
1773
1774    //
1775    // Center this new volume on the origin.
1776    //
1777    float dx0 = -0.5;
1778    float dy0 = -0.5*dy/dx;
1779    float dz0 = -0.5*dz/dx;
1780    volume[index]->move(Vector3(dx0, dy0, dz0));
1781
1782    return result;
1783}
1784
1785/* Load a 3D volume
1786 * index: the index into the volume array, which stores pointers to 3D volume instances
1787 * data: pointer to an array of floats. 
1788 * n_component: the number of scalars for each space point.
1789 *              All component scalars for a point are placed consequtively in data array
1790 * width, height and depth: number of points in each dimension
1791 */
1792void load_volume(int index, int width, int height, int depth,
1793    int n_component, float* data, double vmin, double vmax)
1794{
1795    while (n_volumes <= index) {
1796        volume.push_back(NULL);
1797        n_volumes++;
1798    }
1799
1800    if (volume[index] != NULL) {
1801        delete volume[index];
1802        volume[index] = NULL;
1803    }
1804
1805    volume[index] = new Volume(0.f, 0.f, 0.f, width, height, depth, 1.,
1806                                 n_component, data, vmin, vmax);
1807    assert(volume[index]!=0);
1808}
1809
1810
1811// get a colormap 1D texture by name
1812TransferFunction*
1813get_transfunc(char *name) {
1814    Tcl_HashEntry *entryPtr;
1815    entryPtr = Tcl_FindHashEntry(&tftable, name);
1816    if (entryPtr) {
1817        return (TransferFunction*)Tcl_GetHashValue(entryPtr);
1818    }
1819    return NULL;
1820}
1821
1822
1823//Update the transfer function using local GUI in the non-server mode
1824extern void update_tf_texture(){
1825  glutSetWindow(render_window);
1826
1827  //fprintf(stderr, "tf update\n");
1828  TransferFunction *tf = get_transfunc("default");
1829  if (tf == NULL) return;
1830
1831  float data[256*4];
1832  for(int i=0; i<256; i++){
1833    data[4*i+0] = color_table[i][0];
1834    data[4*i+1] = color_table[i][1];
1835    data[4*i+2] = color_table[i][2];
1836    data[4*i+3] = color_table[i][3];
1837    //fprintf(stderr, "(%f,%f,%f,%f) ", data[4*i+0], data[4*i+1], data[4*i+2], data[4*i+3]);
1838  }
1839
1840  tf->update(data);
1841
1842#ifdef EVENTLOG
1843  float param[3] = {0,0,0};
1844  Event* tmp = new Event(EVENT_ROTATE, param, get_time_interval());
1845  tmp->write(event_log);
1846  delete tmp;
1847#endif
1848}
1849
1850
1851//initialize frame buffer objects for offscreen rendering
1852void init_offscreen_buffer(){
1853
1854  //initialize final fbo for final display
1855  glGenFramebuffersEXT(1, &final_fbo);
1856  glGenTextures(1, &final_color_tex);
1857  glGenRenderbuffersEXT(1, &final_depth_rb);
1858
1859  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
1860
1861  //initialize final color texture
1862  glBindTexture(GL_TEXTURE_2D, final_color_tex);
1863  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1864  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1865#ifdef NV40
1866  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, win_width, win_height, 0,
1867               GL_RGB, GL_INT, NULL);
1868#else
1869  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, win_width, win_height, 0,
1870               GL_RGB, GL_INT, NULL);
1871#endif
1872  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
1873                            GL_COLOR_ATTACHMENT0_EXT,
1874                            GL_TEXTURE_2D, final_color_tex, 0);
1875
1876  // initialize final depth renderbuffer
1877  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, final_depth_rb);
1878  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
1879                           GL_DEPTH_COMPONENT24, win_width, win_height);
1880  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
1881                               GL_DEPTH_ATTACHMENT_EXT,
1882                               GL_RENDERBUFFER_EXT, final_depth_rb);
1883
1884  // Check framebuffer completeness at the end of initialization.
1885  CHECK_FRAMEBUFFER_STATUS();
1886  assert(glGetError()==0);
1887}
1888
1889
1890//resize the offscreen buffer
1891void resize_offscreen_buffer(int w, int h){
1892  win_width = w;
1893  win_height = h;
1894
1895  //fprintf(stderr, "screen_buffer size: %d\n", sizeof(screen_buffer));
1896
1897  if (screen_buffer) {
1898      delete[] screen_buffer;
1899      screen_buffer = NULL;
1900  }
1901  screen_buffer = new unsigned char[4*win_width*win_height];
1902  assert(screen_buffer != NULL);
1903
1904  //delete the current render buffer resources
1905  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
1906  glDeleteTextures(1, &final_color_tex);
1907  glDeleteFramebuffersEXT(1, &final_fbo);
1908
1909  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, final_depth_rb);
1910  glDeleteRenderbuffersEXT(1, &final_depth_rb);
1911fprintf(stdin,"  after glDeleteRenderbuffers\n");
1912
1913  //change the camera setting
1914  cam->set_screen_size(win_width, win_height);
1915  plane_render->set_screen_size(win_width, win_height);
1916
1917  //Reinitialize final fbo for final display
1918  glGenFramebuffersEXT(1, &final_fbo);
1919  glGenTextures(1, &final_color_tex);
1920  glGenRenderbuffersEXT(1, &final_depth_rb);
1921fprintf(stdin,"  after glGenRenderbuffers\n");
1922
1923  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
1924
1925  //initialize final color texture
1926  glBindTexture(GL_TEXTURE_2D, final_color_tex);
1927  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1928  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1929#ifdef NV40
1930  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, win_width, win_height, 0,
1931               GL_RGB, GL_INT, NULL);
1932#else
1933  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, win_width, win_height, 0,
1934               GL_RGB, GL_INT, NULL);
1935#endif
1936  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
1937                            GL_COLOR_ATTACHMENT0_EXT,
1938                            GL_TEXTURE_2D, final_color_tex, 0);
1939fprintf(stdin,"  after glFramebufferTexture2DEXT\n");
1940       
1941  // initialize final depth renderbuffer
1942  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, final_depth_rb);
1943  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
1944                           GL_DEPTH_COMPONENT24, win_width, win_height);
1945  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
1946                               GL_DEPTH_ATTACHMENT_EXT,
1947                               GL_RENDERBUFFER_EXT, final_depth_rb);
1948fprintf(stdin,"  after glFramebufferRenderEXT\n");
1949
1950  // Check framebuffer completeness at the end of initialization.
1951  CHECK_FRAMEBUFFER_STATUS();
1952  assert(glGetError()==0);
1953fprintf(stdin,"  after assert\n");
1954}
1955
1956
1957void init_cg(){
1958    cgSetErrorCallback(cgErrorCallback);
1959    g_context = cgCreateContext();
1960
1961#ifdef NEW_CG
1962    m_posvel_fprog = loadProgram(g_context, CG_PROFILE_FP40, CG_SOURCE, "/opt/nanovis/lib/shaders/update_pos_vel.cg");
1963    m_posvel_timestep_param  = cgGetNamedParameter(m_posvel_fprog, "timestep");
1964    m_posvel_damping_param   = cgGetNamedParameter(m_posvel_fprog, "damping");
1965    m_posvel_gravity_param   = cgGetNamedParameter(m_posvel_fprog, "gravity");
1966    m_posvel_spherePos_param = cgGetNamedParameter(m_posvel_fprog, "spherePos");
1967    m_posvel_sphereVel_param = cgGetNamedParameter(m_posvel_fprog, "sphereVel");
1968#endif
1969}
1970
1971
1972//switch shader to change render mode
1973void switch_shader(int choice){
1974
1975  switch (choice){
1976    case 0:
1977      break;
1978
1979   case 1:
1980      break;
1981     
1982   default:
1983      break;
1984  }
1985}
1986
1987void init_particles(){
1988  //random placement on a slice
1989  float* data = new float[psys->psys_width * psys->psys_height * 4];
1990  bzero(data, sizeof(float)*4* psys->psys_width * psys->psys_height);
1991
1992  for (int i=0; i<psys->psys_width; i++){
1993    for (int j=0; j<psys->psys_height; j++){
1994      int index = i + psys->psys_height*j;
1995      bool particle = rand() % 256 > 150;
1996      //particle = true;
1997      if(particle) /*&& i/float(psys->psys_width)>0.3 && i/float(psys->psys_width)<0.7
1998                      && j/float(psys->psys_height)>0.1 && j/float(psys->psys_height)<0.4)*/
1999      {
2000        //assign any location (x,y,z) in range [0,1]
2001        data[4*index] = lic_slice_x;
2002        data[4*index+1]= j/float(psys->psys_height);
2003        data[4*index+2]= i/float(psys->psys_width);
2004        data[4*index+3]= 30; //shorter life span, quicker iterations   
2005      }
2006      else
2007      {
2008        data[4*index] = 0;
2009        data[4*index+1]= 0;
2010        data[4*index+2]= 0;
2011        data[4*index+3]= 0;     
2012      }
2013    }
2014   }
2015
2016  psys->initialize((Particle*)data);
2017  delete[] data;
2018}
2019
2020
2021//init line integral convolution
2022void init_lic(){
2023  lic = new Lic(NMESH, win_width, win_height, 0.3, g_context, volume[1]->id,
2024                  volume[1]->aspect_ratio_width,
2025                  volume[1]->aspect_ratio_height,
2026                  volume[1]->aspect_ratio_depth);
2027}
2028
2029//init the particle system using vector field volume->[1]
2030void init_particle_system(){
2031   psys = new ParticleSystem(NMESH, NMESH, g_context, volume[1]->id,
2032                   1./volume[1]->aspect_ratio_width,
2033                   1./volume[1]->aspect_ratio_height,
2034                   1./volume[1]->aspect_ratio_depth);
2035
2036   init_particles();    //fill initial particles
2037}
2038
2039
2040void make_test_2D_data(){
2041
2042  int w = 300;
2043  int h = 200;
2044  float* data = new float[w*h];
2045
2046  //procedurally make a gradient plane
2047  for(int j=0; j<h; j++){
2048    for(int i=0; i<w; i++){
2049      data[w*j+i] = float(i)/float(w);
2050    }
2051  }
2052
2053  plane[0] = new Texture2D(w, h, GL_FLOAT, GL_LINEAR, 1, data);
2054
2055  delete[] data;
2056}
2057
2058
2059/*----------------------------------------------------*/
2060void initGL(void)
2061{
2062   system_info();
2063   init_glew();
2064
2065   //buffer to store data read from the screen
2066   if (screen_buffer) {
2067       delete[] screen_buffer;
2068       screen_buffer = NULL;
2069   }
2070   screen_buffer = new unsigned char[4*win_width*win_height];
2071   assert(screen_buffer != NULL);
2072
2073   //create the camera with default setting
2074   cam = new Camera(win_width, win_height,
2075                   live_obj_x, live_obj_y, live_obj_z,
2076                   0., 0., 100.,
2077                   (int)live_rot_x, (int)live_rot_y, (int)live_rot_z);
2078
2079   glEnable(GL_TEXTURE_2D);
2080   glShadeModel(GL_FLAT);
2081   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2082   glClearColor(0,0,0,1);
2083   glClear(GL_COLOR_BUFFER_BIT);
2084
2085   //initialize lighting
2086   GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
2087   GLfloat mat_shininess[] = {30.0};
2088   GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};
2089   GLfloat green_light[] = {0.1, 0.5, 0.1, 1.0};
2090
2091   glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
2092   glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
2093   glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
2094   glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);     
2095   glLightfv(GL_LIGHT1, GL_DIFFUSE, green_light);
2096   glLightfv(GL_LIGHT1, GL_SPECULAR, white_light);     
2097
2098   // init table of transfer functions
2099   Tcl_InitHashTable(&tftable, TCL_STRING_KEYS);
2100
2101   //check if performance query is supported
2102   if(check_query_support()){
2103     //create queries to count number of rendered pixels
2104     perf = new PerfQuery();
2105   }
2106
2107   //load_volume_file(0, "./data/A-apbs-2-out-potential-PE0.dx");
2108   //load_volume_file(0, "./data/nw-AB-Vg=0.000-Vd=1.000-potential.dx");
2109   //load_volume_file(0, "./data/test2.dx");
2110   //load_volume_file(0, "./data/mu-wire-3d.dx"); //I added this line to debug: Wei
2111   //load_volume_file(0, "./data/input_nd_dx_4"); //take a VERY long time?
2112   //load_vector_file(1, "./data/J-wire-vec.dx");
2113   //load_volume_file(1, "./data/mu-wire-3d.dx");  //I added this line to debug: Wei
2114   //load_volume_file(3, "./data/mu-wire-3d.dx");
2115   //load_volume_file(4, "./data/mu-wire-3d.dx");
2116
2117   init_offscreen_buffer();    //frame buffer object for offscreen rendering
2118   init_cg();   //create cg shader context
2119
2120   //create volume renderer and add volumes to it
2121   vol_render = new VolumeRenderer(g_context);
2122
2123   
2124   /*
2125   //I added this to debug : Wei
2126   float tmp_data[4*124];
2127   memset(tmp_data, 0, 4*4*124);
2128   TransferFunction* tmp_tf = new TransferFunction(124, tmp_data);
2129   vol_render->add_volume(volume[0], tmp_tf);
2130   volume[0]->get_cutplane(0)->enabled = false;
2131   volume[0]->get_cutplane(1)->enabled = false;
2132   volume[0]->get_cutplane(2)->enabled = false;
2133
2134   //volume[1]->move(Vector3(0.5, 0.6, 0.7));
2135   //vol_render->add_volume(volume[1], tmp_tf);
2136   */
2137
2138
2139   //create an 2D plane renderer
2140   plane_render = new PlaneRenderer(g_context, win_width, win_height);
2141   make_test_2D_data();
2142
2143   plane_render->add_plane(plane[0], get_transfunc("default"));
2144
2145   assert(glGetError()==0);
2146
2147   //init_particle_system();
2148   //init_lic();
2149}
2150
2151
2152
2153void initTcl(){
2154    interp = Tcl_CreateInterp();
2155    Tcl_MakeSafe(interp);
2156
2157    Tcl_DStringInit(&cmdbuffer);
2158
2159    // manipulate the viewpoint
2160    Tcl_CreateCommand(interp, "camera", CameraCmd,
2161        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2162
2163    // turn on/off cut planes in x/y/z directions
2164    Tcl_CreateCommand(interp, "cutplane", CutplaneCmd,
2165        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2166
2167    // request the legend for a plot (transfer function)
2168    Tcl_CreateCommand(interp, "legend", LegendCmd,
2169        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2170
2171    // change the size of the screen (size of picture generated)
2172    Tcl_CreateCommand(interp, "screen", ScreenCmd,
2173        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2174
2175    // manipulate transfer functions
2176    Tcl_CreateCommand(interp, "transfunc", TransfuncCmd,
2177        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2178
2179    // set the "up" direction for volumes
2180    Tcl_CreateCommand(interp, "up", UpCmd,
2181        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2182
2183    // manipulate volume data
2184    Tcl_CreateCommand(interp, "volume", VolumeCmd,
2185        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2186
2187    // create a default transfer function
2188    if (Tcl_Eval(interp, def_transfunc) != TCL_OK) {
2189        fprintf(stdin, "WARNING: bad default transfer function\n");
2190        fprintf(stdin, Tcl_GetStringResult(interp));
2191    }
2192}
2193
2194
2195void read_screen(){
2196  //glBindTexture(GL_TEXTURE_2D, 0);
2197  //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
2198 
2199  glReadPixels(0, 0, win_width, win_height, GL_RGB, GL_UNSIGNED_BYTE, screen_buffer);
2200  assert(glGetError()==0);
2201}
2202
2203void display();
2204
2205
2206#if DO_RLE
2207char rle[512*512*3];
2208int rle_size;
2209
2210short offsets[512*512*3];
2211int offsets_size;
2212
2213void do_rle(){
2214  int len = win_width*win_height*3;
2215  rle_size = 0;
2216  offsets_size = 0;
2217
2218  int i=0;
2219  while(i<len){
2220    if (screen_buffer[i] == 0) {
2221      int pos = i+1;
2222      while ( (pos<len) && (screen_buffer[pos] == 0)) {
2223        pos++;
2224      }
2225      offsets[offsets_size++] = -(pos - i);
2226      i = pos;
2227    }
2228
2229    else {
2230      int pos;
2231      for (pos = i; (pos<len) && (screen_buffer[pos] != 0); pos++) {
2232        rle[rle_size++] = screen_buffer[pos];
2233      }
2234      offsets[offsets_size++] = (pos - i);
2235      i = pos;
2236    }
2237
2238  }
2239}
2240#endif
2241
2242// used internally to build up the BMP file header
2243// writes an integer value into the header data structure at pos
2244void
2245bmp_header_add_int(unsigned char* header, int& pos, int data)
2246{
2247    header[pos++] = data & 0xff;
2248    header[pos++] = (data >> 8) & 0xff;
2249    header[pos++] = (data >> 16) & 0xff;
2250    header[pos++] = (data >> 24) & 0xff;
2251}
2252
2253void
2254bmp_write(const char* cmd)
2255{
2256    unsigned char header[54];
2257    int pos = 0;
2258    header[pos++] = 'B';
2259    header[pos++] = 'M';
2260
2261    // BE CAREFUL:  BMP files must have an even multiple of 4 bytes
2262    // on each scan line.  If need be, we add padding to each line.
2263    int pad = 0;
2264    if ((3*win_width) % 4 > 0) {
2265        pad = 4 - ((3*win_width) % 4);
2266    }
2267
2268    // file size in bytes
2269    int fsize = (3*win_width+pad)*win_height + sizeof(header);
2270    bmp_header_add_int(header, pos, fsize);
2271
2272    // reserved value (must be 0)
2273    bmp_header_add_int(header, pos, 0);
2274
2275    // offset in bytes to start of bitmap data
2276    bmp_header_add_int(header, pos, sizeof(header));
2277
2278    // size of the BITMAPINFOHEADER
2279    bmp_header_add_int(header, pos, 40);
2280
2281    // width of the image in pixels
2282    bmp_header_add_int(header, pos, win_width);
2283
2284    // height of the image in pixels
2285    bmp_header_add_int(header, pos, win_height);
2286
2287    // 1 plane + (24 bits/pixel << 16)
2288    bmp_header_add_int(header, pos, 1572865);
2289
2290    // no compression
2291    // size of image for compression
2292    bmp_header_add_int(header, pos, 0);
2293    bmp_header_add_int(header, pos, 0);
2294
2295    // x pixels per meter
2296    // y pixels per meter
2297    bmp_header_add_int(header, pos, 0);
2298    bmp_header_add_int(header, pos, 0);
2299
2300    // number of colors used (0 = compute from bits/pixel)
2301    // number of important colors (0 = all colors important)
2302    bmp_header_add_int(header, pos, 0);
2303    bmp_header_add_int(header, pos, 0);
2304
2305    // BE CAREFUL: BMP format wants BGR ordering for screen data
2306    unsigned char* scr = screen_buffer;
2307    for (int row=0; row < win_height; row++) {
2308        for (int col=0; col < win_width; col++) {
2309            unsigned char tmp = scr[2];
2310            scr[2] = scr[0];  // B
2311            scr[0] = tmp;     // R
2312            scr += 3;
2313        }
2314        scr += pad;  // skip over padding already in screen data
2315    }
2316
2317    std::ostringstream result;
2318    result << cmd << " " << fsize << "\n";
2319    write(0, result.str().c_str(), result.str().size());
2320
2321    write(0, header, sizeof(header));
2322    write(0, screen_buffer, (3*win_width+pad)*win_height);
2323}
2324
2325
2326void xinetd_listen(){
2327
2328    int flags = fcntl(0, F_GETFL, 0);
2329    fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
2330
2331    int status = TCL_OK;
2332    int npass = 0;
2333
2334    //
2335    //  Read and execute as many commands as we can from stdin...
2336    //
2337    while (status == TCL_OK) {
2338        //
2339        //  Read the next command from the buffer.  First time through
2340        //  we block here and wait if necessary until a command comes in.
2341        //
2342        //  BE CAREFUL:  Read only one command, up to a newline.
2343        //  The "volume data follows" command needs to be able to read
2344        //  the data immediately following the command, and we shouldn't
2345        //  consume it here.
2346        //
2347        while (1) {
2348            char c = getchar();
2349            if (c <= 0) {
2350                if (npass == 0) {
2351                    exit(0);  // EOF -- we're done!
2352                } else {
2353                    break;
2354                }
2355            }
2356            Tcl_DStringAppend(&cmdbuffer, &c, 1);
2357
2358            if (c=='\n' && Tcl_CommandComplete(Tcl_DStringValue(&cmdbuffer))) {
2359                break;
2360            }
2361        }
2362
2363        // no command? then we're done for now
2364        if (Tcl_DStringLength(&cmdbuffer) == 0) {
2365            break;
2366        }
2367
2368        // back to original flags during command evaluation...
2369        fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
2370
2371        status = Tcl_Eval(interp, Tcl_DStringValue(&cmdbuffer));
2372        Tcl_DStringSetLength(&cmdbuffer, 0);
2373
2374        // non-blocking for next read -- we might not get anything
2375        fcntl(0, F_SETFL, flags | O_NONBLOCK);
2376        npass++;
2377    }
2378    fcntl(0, F_SETFL, flags);
2379
2380    if (status != TCL_OK) {
2381        std::ostringstream errmsg;
2382        errmsg << "ERROR: " << Tcl_GetStringResult(interp) << std::endl;
2383        write(0, errmsg.str().c_str(), errmsg.str().size());
2384        return;
2385    }
2386
2387    //
2388    //  Generate the latest frame and send it back to the client
2389    //
2390    display();
2391
2392#if DO_RLE
2393    do_rle();
2394    int sizes[2] = {  offsets_size*sizeof(offsets[0]), rle_size };
2395    fprintf(stderr, "Writing %d,%d\n", sizes[0], sizes[1]); fflush(stderr);
2396    write(0, &sizes, sizeof(sizes));
2397    write(0, offsets, offsets_size*sizeof(offsets[0]));
2398    write(0, rle, rle_size);    //unsigned byte
2399#else
2400    bmp_write("nv>image -bytes");
2401#endif
2402}
2403
2404
2405/*
2406//draw vectors
2407void draw_arrows(){
2408  glColor4f(0.,0.,1.,1.);
2409  for(int i=0; i<NMESH; i++){
2410    for(int j=0; j<NMESH; j++){
2411      Vector2 v = grid.get(i, j);
2412
2413      int x1 = i*DM;
2414      int y1 = j*DM;
2415
2416      int x2 = x1 + v.x;
2417      int y2 = y1 + v.y;
2418
2419      glBegin(GL_LINES);
2420        glVertex2d(x1, y1);
2421        glVertex2d(x2, y2);
2422      glEnd();
2423    }
2424  }
2425}
2426*/
2427
2428
2429/*----------------------------------------------------*/
2430void idle(){
2431  glutSetWindow(render_window);
2432
2433 
2434  /*
2435  struct timespec ts;
2436  ts.tv_sec = 0;
2437  ts.tv_nsec = 300000000;
2438  nanosleep(&ts, 0);
2439  */
2440 
2441
2442#ifdef XINETD
2443  xinetd_listen();
2444#else
2445  glutPostRedisplay();
2446#endif
2447}
2448
2449
2450void display_offscreen_buffer(){
2451   glEnable(GL_TEXTURE_2D);
2452   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
2453   glBindTexture(GL_TEXTURE_2D, final_color_tex);
2454
2455   glViewport(0, 0, win_width, win_height);
2456   glMatrixMode(GL_PROJECTION);
2457   glLoadIdentity();
2458   gluOrtho2D(0, win_width, 0, win_height);
2459   glMatrixMode(GL_MODELVIEW);
2460   glLoadIdentity();
2461
2462   glColor3f(1.,1.,1.);         //MUST HAVE THIS LINE!!!
2463   glBegin(GL_QUADS);
2464   glTexCoord2f(0, 0); glVertex2f(0, 0);
2465   glTexCoord2f(1, 0); glVertex2f(win_width, 0);
2466   glTexCoord2f(1, 1); glVertex2f(win_width, win_height);
2467   glTexCoord2f(0, 1); glVertex2f(0, win_height);
2468   glEnd();
2469}
2470
2471void offscreen_buffer_capture(){
2472   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
2473}
2474
2475void draw_bounding_box(float x0, float y0, float z0,
2476                float x1, float y1, float z1,
2477                float r, float g, float b, float line_width)
2478{
2479        glDisable(GL_TEXTURE_2D);
2480
2481        glColor4d(r, g, b, 1.0);
2482        glLineWidth(line_width);
2483       
2484        glBegin(GL_LINE_LOOP);
2485
2486                glVertex3d(x0, y0, z0);
2487                glVertex3d(x1, y0, z0);
2488                glVertex3d(x1, y1, z0);
2489                glVertex3d(x0, y1, z0);
2490               
2491        glEnd();
2492
2493        glBegin(GL_LINE_LOOP);
2494
2495                glVertex3d(x0, y0, z1);
2496                glVertex3d(x1, y0, z1);
2497                glVertex3d(x1, y1, z1);
2498                glVertex3d(x0, y1, z1);
2499               
2500        glEnd();
2501
2502
2503        glBegin(GL_LINE_LOOP);
2504
2505                glVertex3d(x0, y0, z0);
2506                glVertex3d(x0, y0, z1);
2507                glVertex3d(x0, y1, z1);
2508                glVertex3d(x0, y1, z0);
2509               
2510        glEnd();
2511
2512        glBegin(GL_LINE_LOOP);
2513
2514                glVertex3d(x1, y0, z0);
2515                glVertex3d(x1, y0, z1);
2516                glVertex3d(x1, y1, z1);
2517                glVertex3d(x1, y1, z0);
2518               
2519        glEnd();
2520
2521        glEnable(GL_TEXTURE_2D);
2522}
2523
2524
2525
2526int particle_distance_sort(const void* a, const void* b){
2527  if((*((Particle*)a)).aux > (*((Particle*)b)).aux)
2528    return -1;
2529  else
2530    return 1;
2531}
2532
2533
2534void soft_read_verts(){
2535  glReadPixels(0, 0, psys->psys_width, psys->psys_height, GL_RGB, GL_FLOAT, vert);
2536  //fprintf(stderr, "soft_read_vert");
2537
2538  //cpu sort the distance 
2539  Particle* p = (Particle*) malloc(sizeof(Particle)* psys->psys_width * psys->psys_height);
2540  for(int i=0; i<psys->psys_width * psys->psys_height; i++){
2541    float x = vert[3*i];
2542    float y = vert[3*i+1];
2543    float z = vert[3*i+2];
2544
2545    float dis = (x-live_obj_x)*(x-live_obj_x) + (y-live_obj_y)*(y-live_obj_y) + (z-live_obj_z)*(z-live_obj_z);
2546    p[i].x = x;
2547    p[i].y = y;
2548    p[i].z = z;
2549    p[i].aux = dis;
2550  }
2551
2552  qsort(p, psys->psys_width * psys->psys_height, sizeof(Particle), particle_distance_sort);
2553
2554  for(int i=0; i<psys->psys_width * psys->psys_height; i++){
2555    vert[3*i] = p[i].x;
2556    vert[3*i+1] = p[i].y;
2557    vert[3*i+2] = p[i].z;
2558  }
2559
2560  free(p);
2561}
2562
2563
2564//display the content of a texture as a screen aligned quad
2565void display_texture(NVISid tex, int width, int height){
2566   glPushMatrix();
2567
2568   glEnable(GL_TEXTURE_2D);
2569   glBindTexture(GL_TEXTURE_RECTANGLE_NV, tex);
2570
2571   glViewport(0, 0, width, height);
2572   glMatrixMode(GL_PROJECTION);
2573   glLoadIdentity();
2574   gluOrtho2D(0, width, 0, height);
2575   glMatrixMode(GL_MODELVIEW);
2576   glLoadIdentity();
2577
2578   cgGLBindProgram(m_passthru_fprog);
2579   cgGLEnableProfile(CG_PROFILE_FP30);
2580
2581   cgGLSetParameter4f(m_passthru_scale_param, 1.0, 1.0, 1.0, 1.0);
2582   cgGLSetParameter4f(m_passthru_bias_param, 0.0, 0.0, 0.0, 0.0);
2583   
2584   draw_quad(width, height, width, height);
2585   cgGLDisableProfile(CG_PROFILE_FP30);
2586
2587   glPopMatrix();
2588
2589   assert(glGetError()==0);
2590}
2591
2592
2593//draw vertices in the main memory
2594void soft_display_verts(){
2595  glDisable(GL_TEXTURE_2D);
2596  glEnable(GL_BLEND);
2597
2598  glPointSize(0.5);
2599  glColor4f(0,0.8,0.8,0.6);
2600  glBegin(GL_POINTS);
2601  for(int i=0; i<psys->psys_width * psys->psys_height; i++){
2602    glVertex3f(vert[3*i], vert[3*i+1], vert[3*i+2]);
2603  }
2604  glEnd();
2605  //fprintf(stderr, "soft_display_vert");
2606}
2607
2608
2609#if 0
2610
2611//oddeven sort on GPU
2612void sortstep()
2613{
2614    // perform one step of the current sorting algorithm
2615
2616        /*
2617    // swap buffers
2618    int sourceBuffer = targetBuffer;
2619    targetBuffer = (targetBuffer+1)%2;   
2620    int pstage = (1<<stage);
2621    int ppass  = (1<<pass);
2622    int activeBitonicShader = 0;
2623
2624#ifdef _WIN32
2625    buffer->BindBuffer(wglTargets[sourceBuffer]);
2626#else
2627    buffer->BindBuffer(glTargets[sourceBuffer]);
2628#endif
2629    if (buffer->IsDoubleBuffered()) glDrawBuffer(glTargets[targetBuffer]);
2630    */
2631
2632    checkGLError("after db");
2633
2634    int pstage = (1<<stage);
2635    int ppass  = (1<<pass);
2636    //int activeBitonicShader = 0;
2637
2638    // switch on correct sorting shader
2639    oddevenMergeSort.bind();
2640    glUniform3fARB(oddevenMergeSort.getUniformLocation("Param1"), float(pstage+pstage),
2641                   float(ppass%pstage), float((pstage+pstage)-(ppass%pstage)-1));
2642    glUniform3fARB(oddevenMergeSort.getUniformLocation("Param2"), float(psys_width), float(psys_height), float(ppass));
2643    glUniform1iARB(oddevenMergeSort.getUniformLocation("Data"), 0);
2644    staticdebugmsg("sort","stage "<<pstage<<" pass "<<ppass);
2645
2646    // This clear is not necessary for sort to function. But if we are in interactive mode
2647    // unused parts of the texture that are visible will look bad.
2648    //if (!perfTest) glClear(GL_COLOR_BUFFER_BIT);
2649
2650    //buffer->Bind();
2651    //buffer->EnableTextureTarget();
2652
2653    // initiate the sorting step on the GPU
2654    // a full-screen quad
2655    glBegin(GL_QUADS);
2656    /*
2657    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,0.0f,0.0f,0.0f,1.0f); glVertex2f(-1.0f,-1.0f);
2658    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,float(psys_width),0.0f,1.0f,1.0f); glVertex2f(1.0f,-1.0f);
2659    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,float(psys_width),float(psys_height),1.0f,0.0f); glVertex2f(1.0f,1.0f);       
2660    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,0.0f,float(psys_height),0.0f,0.0f); glVertex2f(-1.0f,1.0f);   
2661    */
2662    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,0.0f,0.0f,0.0f,1.0f); glVertex2f(0.,0.);       
2663    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,float(psys_width),0.0f,1.0f,1.0f); glVertex2f(float(psys_width), 0.);
2664    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,float(psys_width),float(psys_height),1.0f,0.0f); glVertex2f(float(psys_width), float(psys_height));   
2665    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,0.0f,float(psys_height),0.0f,0.0f); glVertex2f(0., float(psys_height));       
2666    glEnd();
2667
2668
2669    // switch off sorting shader
2670    oddevenMergeSort.release();
2671
2672    //buffer->DisableTextureTarget();
2673
2674    assert(glGetError()==0);
2675}
2676
2677#endif
2678
2679
2680void draw_3d_axis(){
2681  glDisable(GL_TEXTURE_2D);
2682  glEnable(GL_DEPTH_TEST);
2683
2684        //draw axes
2685        GLUquadric *obj;
2686        obj = gluNewQuadric();
2687       
2688        glDepthFunc(GL_LESS);
2689        glEnable(GL_DEPTH_TEST);
2690        glDisable(GL_BLEND);
2691
2692        int segments = 50;
2693
2694        glColor3f(0.8, 0.8, 0.8);
2695        glPushMatrix();
2696        glTranslatef(0.4, 0., 0.);
2697        glRotatef(90, 1, 0, 0);
2698        glRotatef(180, 0, 1, 0);
2699        glScalef(0.0005, 0.0005, 0.0005);
2700        glutStrokeCharacter(GLUT_STROKE_ROMAN, 'x');
2701        glPopMatrix(); 
2702
2703        glPushMatrix();
2704        glTranslatef(0., 0.4, 0.);
2705        glRotatef(90, 1, 0, 0);
2706        glRotatef(180, 0, 1, 0);
2707        glScalef(0.0005, 0.0005, 0.0005);
2708        glutStrokeCharacter(GLUT_STROKE_ROMAN, 'y');
2709        glPopMatrix(); 
2710
2711        glPushMatrix();
2712        glTranslatef(0., 0., 0.4);
2713        glRotatef(90, 1, 0, 0);
2714        glRotatef(180, 0, 1, 0);
2715        glScalef(0.0005, 0.0005, 0.0005);
2716        glutStrokeCharacter(GLUT_STROKE_ROMAN, 'z');
2717        glPopMatrix(); 
2718
2719        glEnable(GL_LIGHTING);
2720        glEnable(GL_LIGHT0);
2721
2722        glColor3f(0.2, 0.2, 0.8);
2723        glPushMatrix();
2724        glutSolidSphere(0.02, segments, segments );
2725        glPopMatrix();
2726
2727        glPushMatrix();
2728        glRotatef(-90, 1, 0, 0);       
2729        gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
2730        glPopMatrix(); 
2731
2732        glPushMatrix();
2733        glTranslatef(0., 0.3, 0.);
2734        glRotatef(-90, 1, 0, 0);       
2735        gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
2736        glPopMatrix(); 
2737
2738        glPushMatrix();
2739        glRotatef(90, 0, 1, 0);
2740        gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
2741        glPopMatrix(); 
2742
2743        glPushMatrix();
2744        glTranslatef(0.3, 0., 0.);
2745        glRotatef(90, 0, 1, 0);
2746        gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
2747        glPopMatrix(); 
2748
2749        glPushMatrix();
2750        gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
2751        glPopMatrix(); 
2752
2753        glPushMatrix();
2754        glTranslatef(0., 0., 0.3);
2755        gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
2756        glPopMatrix(); 
2757
2758        glDisable(GL_LIGHTING);
2759        glDisable(GL_DEPTH_TEST);
2760        gluDeleteQuadric(obj);
2761
2762  glEnable(GL_TEXTURE_2D);
2763  glDisable(GL_DEPTH_TEST);
2764}
2765
2766
2767void draw_axis(){
2768
2769  glDisable(GL_TEXTURE_2D);
2770  glEnable(GL_DEPTH_TEST);
2771
2772  //red x
2773  glColor3f(1,0,0);
2774  glBegin(GL_LINES);
2775    glVertex3f(0,0,0);
2776    glVertex3f(1.5,0,0);
2777  glEnd();
2778
2779  //blue y
2780  glColor3f(0,0,1);
2781  glBegin(GL_LINES);
2782    glVertex3f(0,0,0);
2783    glVertex3f(0,1.5,0);
2784  glEnd();
2785
2786  //green z
2787  glColor3f(0,1,0);
2788  glBegin(GL_LINES);
2789    glVertex3f(0,0,0);
2790    glVertex3f(0,0,1.5);
2791  glEnd();
2792
2793  glEnable(GL_TEXTURE_2D);
2794  glDisable(GL_DEPTH_TEST);
2795}
2796
2797
2798
2799/*----------------------------------------------------*/
2800void display()
2801{
2802
2803   assert(glGetError()==0);
2804
2805   //lic->convolve(); //flow line integral convolution
2806   //psys->advect(); //advect particles
2807
2808   offscreen_buffer_capture();  //enable offscreen render
2809   //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); //enable onscreen render
2810
2811   //start final rendering
2812   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear screen
2813
2814   if (volume_mode) {
2815     //3D rendering mode
2816     glEnable(GL_TEXTURE_2D);
2817     glEnable(GL_DEPTH_TEST);
2818
2819     //camera setting activated
2820     cam->activate();
2821
2822     //set up the orientation of items in the scene.
2823     glPushMatrix();
2824       switch (updir) {
2825         case 1:  // x
2826               glRotatef(90, 0, 0, 1);
2827               glRotatef(90, 1, 0, 0);
2828           break;
2829
2830         case 2:  // y
2831           // this is the default
2832           break;
2833
2834         case 3:  // z
2835               glRotatef(-90, 1, 0, 0);
2836               glRotatef(-90, 0, 0, 1);
2837           break;
2838
2839         case -1:  // -x
2840               glRotatef(-90, 0, 0, 1);
2841           break;
2842
2843         case -2:  // -y
2844               glRotatef(180, 0, 0, 1);
2845               glRotatef(-90, 0, 1, 0);
2846           break;
2847
2848         case -3:  // -z
2849               glRotatef(90, 1, 0, 0);
2850           break;
2851       }
2852
2853       //now render things in the scene
2854       draw_3d_axis();
2855
2856       //lic->render();         //display the line integral convolution result
2857       //soft_display_verts();
2858       //perf->enable();
2859       //  psys->render();
2860       //perf->disable();
2861       //fprintf(stderr, "particle pixels: %d\n", perf->get_pixel_count());
2862       //perf->reset();
2863
2864       perf->enable();
2865         vol_render->render_all();
2866       perf->disable();
2867
2868     glPopMatrix();
2869   }
2870   else{
2871     //2D rendering mode
2872     perf->enable();
2873       plane_render->render();
2874     perf->disable();
2875   }
2876
2877
2878#ifdef XINETD
2879   float cost  = perf->get_pixel_count();
2880   write(3, &cost, sizeof(cost));
2881#endif
2882   perf->reset();
2883
2884   display_offscreen_buffer(); //display the final rendering on screen
2885
2886#ifdef XINETD
2887   read_screen();
2888#else
2889   //read_screen();
2890#endif   
2891
2892   glutSwapBuffers();
2893}
2894
2895
2896void mouse(int button, int state, int x, int y){
2897  if(button==GLUT_LEFT_BUTTON){
2898
2899    if(state==GLUT_DOWN){
2900      left_last_x = x;
2901      left_last_y = y;
2902      left_down = true;
2903      right_down = false;
2904    }
2905    else{
2906      left_down = false;
2907      right_down = false;
2908    }
2909  }
2910  else{
2911    //fprintf(stderr, "right mouse\n");
2912
2913    if(state==GLUT_DOWN){
2914      //fprintf(stderr, "right mouse down\n");
2915      right_last_x = x;
2916      right_last_y = y;
2917      left_down = false;
2918      right_down = true;
2919    }
2920    else{
2921      //fprintf(stderr, "right mouse up\n");
2922      left_down = false;
2923      right_down = false;
2924    }
2925  }
2926}
2927
2928
2929void update_rot(int delta_x, int delta_y){
2930        live_rot_x += delta_x;
2931        live_rot_y += delta_y;
2932
2933        if(live_rot_x > 360.0)
2934                live_rot_x -= 360.0;   
2935        else if(live_rot_x < -360.0)
2936                live_rot_x += 360.0;
2937
2938        if(live_rot_y > 360.0)
2939                live_rot_y -= 360.0;   
2940        else if(live_rot_y < -360.0)
2941                live_rot_y += 360.0;
2942
2943        cam->rotate(live_rot_x, live_rot_y, live_rot_z);
2944}
2945
2946
2947void update_trans(int delta_x, int delta_y, int delta_z){
2948        live_obj_x += delta_x*0.03;
2949        live_obj_y += delta_y*0.03;
2950        live_obj_z += delta_z*0.03;
2951}
2952
2953void end_service();
2954
2955void keyboard(unsigned char key, int x, int y){
2956 
2957   bool log = false;
2958
2959   switch (key){
2960        case 'q':
2961#ifdef XINETD
2962                end_service();
2963#endif
2964                exit(0);
2965                break;
2966        case '+':
2967                lic_slice_z+=0.05;
2968                lic->set_offset(lic_slice_z);
2969                break;
2970        case '-':
2971                lic_slice_z-=0.05;
2972                lic->set_offset(lic_slice_z);
2973                break;
2974        case ',':
2975                lic_slice_x+=0.05;
2976                init_particles();
2977                break;
2978        case '.':
2979                lic_slice_x-=0.05;
2980                init_particles();
2981                break;
2982        case '1':
2983                advect = true;
2984                break;
2985        case '2':
2986                psys_x+=0.05;
2987                break;
2988        case '3':
2989                psys_x-=0.05;
2990                break;
2991        case 'w': //zoom out
2992                live_obj_z-=0.05;
2993                log = true;
2994                cam->move(live_obj_x, live_obj_y, live_obj_z);
2995                break;
2996        case 's': //zoom in
2997                live_obj_z+=0.05;
2998                log = true;
2999                cam->move(live_obj_x, live_obj_y, live_obj_z);
3000                break;
3001        case 'a': //left
3002                live_obj_x-=0.05;
3003                log = true;
3004                cam->move(live_obj_x, live_obj_y, live_obj_z);
3005                break;
3006        case 'd': //right
3007                live_obj_x+=0.05;
3008                log = true;
3009                cam->move(live_obj_x, live_obj_y, live_obj_z);
3010                break;
3011        case 'i':
3012                init_particles();
3013                break;
3014        case 'v':
3015                vol_render->switch_volume_mode();
3016                break;
3017        case 'b':
3018                vol_render->switch_slice_mode();
3019                break;
3020        case 'n':
3021                resize_offscreen_buffer(win_width*2, win_height*2);
3022                break;
3023        case 'm':
3024                resize_offscreen_buffer(win_width/2, win_height/2);
3025                break;
3026
3027        default:
3028                break;
3029    }   
3030
3031#ifdef EVENTLOG
3032   if(log){
3033     float param[3] = {live_obj_x, live_obj_y, live_obj_z};
3034     Event* tmp = new Event(EVENT_MOVE, param, get_time_interval());
3035     tmp->write(event_log);
3036     delete tmp;
3037   }
3038#endif
3039}
3040
3041void motion(int x, int y){
3042
3043    int old_x, old_y;   
3044
3045    if(left_down){
3046      old_x = left_last_x;
3047      old_y = left_last_y;   
3048    }
3049    else if(right_down){
3050      old_x = right_last_x;
3051      old_y = right_last_y;   
3052    }
3053
3054    int delta_x = x - old_x;
3055    int delta_y = y - old_y;
3056
3057    //more coarse event handling
3058    //if(abs(delta_x)<10 && abs(delta_y)<10)
3059      //return;
3060
3061    if(left_down){
3062      left_last_x = x;
3063      left_last_y = y;
3064
3065      update_rot(-delta_y, -delta_x);
3066    }
3067    else if (right_down){
3068      //fprintf(stderr, "right mouse motion (%d,%d)\n", x, y);
3069
3070      right_last_x = x;
3071      right_last_y = y;
3072
3073      update_trans(0, 0, delta_x);
3074    }
3075
3076#ifdef EVENTLOG
3077    float param[3] = {live_rot_x, live_rot_y, live_rot_z};
3078    Event* tmp = new Event(EVENT_ROTATE, param, get_time_interval());
3079    tmp->write(event_log);
3080    delete tmp;
3081#endif
3082
3083    glutPostRedisplay();
3084}
3085
3086#ifdef XINETD
3087void init_service(){
3088  //open log and map stderr to log file
3089  xinetd_log = fopen("log.txt", "w");
3090  close(2);
3091  dup2(fileno(xinetd_log), 2);
3092  dup2(2,1);
3093
3094  //flush junk
3095  fflush(stdout);
3096  fflush(stderr);
3097}
3098
3099void end_service(){
3100  //close log file
3101  fclose(xinetd_log);
3102}
3103#endif
3104
3105void init_event_log(){
3106  event_log = fopen("event.txt", "w");
3107  assert(event_log!=0);
3108
3109  struct timeval time;
3110  gettimeofday(&time, NULL);
3111  cur_time = time.tv_sec*1000. + time.tv_usec/1000.;
3112}
3113
3114void end_event_log(){
3115  fclose(event_log);
3116}
3117
3118double get_time_interval(){
3119  struct timeval time;
3120  gettimeofday(&time, NULL);
3121  double new_time = time.tv_sec*1000. + time.tv_usec/1000.;
3122
3123  double interval = new_time - cur_time;
3124  cur_time = new_time;
3125  return interval;
3126}
3127
3128
3129/*----------------------------------------------------*/
3130int main(int argc, char** argv)
3131{
3132  sleep(30);
3133#ifdef XINETD
3134   init_service();
3135#endif
3136
3137   glutInit(&argc, argv);
3138   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
3139   
3140   glutInitWindowSize(win_width, win_height);
3141   glutInitWindowPosition(10, 10);
3142   render_window = glutCreateWindow(argv[0]);
3143   glutDisplayFunc(display);
3144
3145#ifndef XINETD
3146   //MainTransferFunctionWindow * tf_window;
3147   //tf_window = new MainTransferFunctionWindow();
3148   //tf_window->mainInit();
3149
3150   glutMouseFunc(mouse);
3151   glutMotionFunc(motion);
3152   glutKeyboardFunc(keyboard);
3153#endif
3154   glutIdleFunc(idle);
3155   glutReshapeFunc(resize_offscreen_buffer);
3156
3157   initGL();
3158   initTcl();
3159
3160#ifdef EVENTLOG
3161   init_event_log();
3162#endif
3163   //event loop
3164   glutMainLoop();
3165#ifdef EVENTLOG
3166   end_event_log();
3167#endif
3168
3169#ifdef XINETD
3170   end_service();
3171#endif
3172
3173   return 0;
3174}
Note: See TracBrowser for help on using the repository browser.