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

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

Fixed a bug in the BMP encoding of the transmitted image. BMP wants
all data in BGR (backwards) form, and it pads each scan line to an
even multiple of 4 bytes. The OpenGL calls that fetch the screen
do the same padding, but they put out data in RGB. We needed to
swap the bytes and communicate the proper padding information for
screen sizes that weren't an even multiple of 4.

File size: 91.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        }
1310    } while (!fin.eof());
1311
1312    // read data points
1313    if (!fin.eof()) {
1314        Rappture::Mesh1D xgrid(x0, x0+nx*dx, nx);
1315        Rappture::Mesh1D ygrid(y0, y0+ny*dy, ny);
1316        Rappture::Mesh1D zgrid(z0, z0+nz*dz, nz);
1317        Rappture::FieldRect3D xfield(xgrid, ygrid, zgrid);
1318        Rappture::FieldRect3D yfield(xgrid, ygrid, zgrid);
1319        Rappture::FieldRect3D zfield(xgrid, ygrid, zgrid);
1320
1321        double vx, vy, vz;
1322        int nread = 0;
1323        for (int ix=0; ix < nx; ix++) {
1324            for (int iy=0; iy < ny; iy++) {
1325                for (int iz=0; iz < nz; iz++) {
1326                    if (fin.eof() || nread > npts) {
1327                        break;
1328                    }
1329                    fin.getline(line,sizeof(line)-1);
1330                    if (sscanf(line, "%lg %lg %lg", &vx, &vy, &vz) == 3) {
1331                        int nindex = iz*nx*ny + iy*nx + ix;
1332                        xfield.define(nindex, vx);
1333                        yfield.define(nindex, vy);
1334                        zfield.define(nindex, vz);
1335                        nread++;
1336                    }
1337                }
1338            }
1339        }
1340
1341        // make sure that we read all of the expected points
1342        if (nread != nx*ny*nz) {
1343            std::cerr << "inconsistent data: expected " << nx*ny*nz << " points but found " << nread << " points" << std::endl;
1344            return;
1345        }
1346
1347        // figure out a good mesh spacing
1348        int nsample = 30;
1349        dx = xfield.rangeMax(Rappture::xaxis) - xfield.rangeMin(Rappture::xaxis);
1350        dy = xfield.rangeMax(Rappture::yaxis) - xfield.rangeMin(Rappture::yaxis);
1351        dz = xfield.rangeMax(Rappture::zaxis) - xfield.rangeMin(Rappture::zaxis);
1352        double dmin = pow((dx*dy*dz)/(nsample*nsample*nsample), 0.333);
1353
1354        nx = (int)ceil(dx/dmin);
1355        ny = (int)ceil(dy/dmin);
1356        nz = (int)ceil(dz/dmin);
1357
1358#ifndef NV40
1359        // must be an even power of 2 for older cards
1360            nx = (int)pow(2.0, ceil(log10((double)nx)/log10(2.0)));
1361            ny = (int)pow(2.0, ceil(log10((double)ny)/log10(2.0)));
1362            nz = (int)pow(2.0, ceil(log10((double)nz)/log10(2.0)));
1363#endif
1364
1365        float *data = new float[3*nx*ny*nz];
1366
1367        std::cout << "generating " << nx << "x" << ny << "x" << nz << " = " << nx*ny*nz << " points" << std::endl;
1368
1369        // generate the uniformly sampled data that we need for a volume
1370        double vmin = 0.0;
1371        double vmax = 0.0;
1372        int ngen = 0;
1373        for (int iz=0; iz < nz; iz++) {
1374            double zval = z0 + iz*dmin;
1375            for (int iy=0; iy < ny; iy++) {
1376                double yval = y0 + iy*dmin;
1377                for (int ix=0; ix < nx; ix++) {
1378                    double xval = x0 + ix*dmin;
1379
1380                    vx = xfield.value(xval,yval,zval);
1381                    vy = yfield.value(xval,yval,zval);
1382                    vz = zfield.value(xval,yval,zval);
1383                    //vx = 1;
1384                    //vy = 1;
1385                    vz = 0;
1386                    double vm = sqrt(vx*vx + vy*vy + vz*vz);
1387
1388                    if (vm < vmin) { vmin = vm; }
1389                    if (vm > vmax) { vmax = vm; }
1390
1391                    data[ngen++] = vx;
1392                    data[ngen++] = vy;
1393                    data[ngen++] = vz;
1394                }
1395            }
1396        }
1397
1398        ngen = 0;
1399        for (ngen=0; ngen < npts; ngen++) {
1400            data[ngen] = (data[ngen]/(2.0*vmax) + 0.5);
1401        }
1402
1403        load_volume(index, nx, ny, nz, 3, data, vmin, vmax);
1404        delete [] data;
1405    } else {
1406        std::cerr << "WARNING: data not found in file " << fname << std::endl;
1407    }
1408}
1409
1410
1411/* Load a 3D volume from a dx-format file
1412 */
1413Rappture::Outcome
1414load_volume_file(int index, char *fname) {
1415    Rappture::Outcome result;
1416
1417    Rappture::MeshTri2D xymesh;
1418    int dummy, nx, ny, nz, nxy, npts;
1419    double x0, y0, z0, dx, dy, dz, ddx, ddy, ddz;
1420    char line[128], type[128], *start;
1421    std::ifstream fin(fname);
1422
1423    int isrect = 1;
1424
1425    do {
1426        fin.getline(line,sizeof(line)-1);
1427        for (start=&line[0]; *start == ' ' || *start == '\t'; start++)
1428            ;  // skip leading blanks
1429
1430        if (*start != '#') {  // skip comment lines
1431            if (sscanf(start, "object %d class gridpositions counts %d %d %d", &dummy, &nx, &ny, &nz) == 4) {
1432                // found grid size
1433                isrect = 1;
1434            }
1435            else if (sscanf(start, "object %d class array type float rank 1 shape 3 items %d data follows", &dummy, &nxy) == 2) {
1436                isrect = 0;
1437                double xx, yy, zz;
1438                for (int i=0; i < nxy; i++) {
1439                    fin.getline(line,sizeof(line)-1);
1440                    if (sscanf(line, "%lg %lg %lg", &xx, &yy, &zz) == 3) {
1441                        xymesh.addNode( Rappture::Node2D(xx,yy) );
1442                    }
1443                }
1444
1445                char fpts[128];
1446                sprintf(fpts, "/tmp/tmppts%d", getpid());
1447                char fcells[128];
1448                sprintf(fcells, "/tmp/tmpcells%d", getpid());
1449
1450                std::ofstream ftmp(fpts);
1451                // save corners of bounding box first, to work around meshing
1452                // problems in voronoi utility
1453                ftmp << xymesh.rangeMin(Rappture::xaxis) << " "
1454                     << xymesh.rangeMin(Rappture::yaxis) << std::endl;
1455                ftmp << xymesh.rangeMax(Rappture::xaxis) << " "
1456                     << xymesh.rangeMin(Rappture::yaxis) << std::endl;
1457                ftmp << xymesh.rangeMax(Rappture::xaxis) << " "
1458                     << xymesh.rangeMax(Rappture::yaxis) << std::endl;
1459                ftmp << xymesh.rangeMin(Rappture::xaxis) << " "
1460                     << xymesh.rangeMax(Rappture::yaxis) << std::endl;
1461                for (int i=0; i < nxy; i++) {
1462                    ftmp << xymesh.atNode(i).x() << " " << xymesh.atNode(i).y() << std::endl;
1463                }
1464                ftmp.close();
1465
1466                char cmdstr[512];
1467                sprintf(cmdstr, "voronoi -t < %s > %s", fpts, fcells);
1468                if (system(cmdstr) == 0) {
1469                    int cx, cy, cz;
1470                    std::ifstream ftri(fcells);
1471                    while (!ftri.eof()) {
1472                        ftri.getline(line,sizeof(line)-1);
1473                        if (sscanf(line, "%d %d %d", &cx, &cy, &cz) == 3) {
1474                            if (cx >= 4 && cy >= 4 && cz >= 4) {
1475                                // skip first 4 boundary points
1476                                xymesh.addCell(cx-4, cy-4, cz-4);
1477                            }
1478                        }
1479                    }
1480                    ftri.close();
1481                } else {
1482                    return result.error("triangularization failed");
1483                }
1484
1485                sprintf(cmdstr, "rm -f %s %s", fpts, fcells);
1486                system(cmdstr);
1487            }
1488            else if (sscanf(start, "object %d class regulararray count %d", &dummy, &nz) == 2) {
1489                // found z-grid
1490            }
1491            else if (sscanf(start, "origin %lg %lg %lg", &x0, &y0, &z0) == 3) {
1492                // found origin
1493            }
1494            else if (sscanf(start, "delta %lg %lg %lg", &ddx, &ddy, &ddz) == 3) {
1495                // found one of the delta lines
1496                if (ddx != 0.0) { dx = ddx; }
1497                else if (ddy != 0.0) { dy = ddy; }
1498                else if (ddz != 0.0) { dz = ddz; }
1499            }
1500            else if (sscanf(start, "object %d class array type %s rank 0 items %d data follows", &dummy, type, &npts) == 3) {
1501                if (isrect && (npts != nx*ny*nz)) {
1502                    char mesg[256];
1503                    sprintf(mesg,"inconsistent data: expected %d points but found %d points", nx*ny*nz, npts);
1504                    return result.error(mesg);
1505                }
1506                else if (!isrect && (npts != nxy*nz)) {
1507                    char mesg[256];
1508                    sprintf(mesg,"inconsistent data: expected %d points but found %d points", nxy*nz, npts);
1509                    return result.error(mesg);
1510                }
1511                break;
1512            }
1513        }
1514    } while (!fin.eof());
1515
1516    // read data points
1517    if (!fin.eof()) {
1518        if (isrect) {
1519            Rappture::Mesh1D xgrid(x0, x0+nx*dx, nx);
1520            Rappture::Mesh1D ygrid(y0, y0+ny*dy, ny);
1521            Rappture::Mesh1D zgrid(z0, z0+nz*dz, nz);
1522            Rappture::FieldRect3D field(xgrid, ygrid, zgrid);
1523
1524            double dval;
1525            int nread = 0;
1526            for (int ix=0; ix < nx; ix++) {
1527                for (int iy=0; iy < ny; iy++) {
1528                    for (int iz=0; iz < nz; iz++) {
1529                        if (fin.eof() || nread > npts) {
1530                            break;
1531                        }
1532                        fin.getline(line,sizeof(line)-1);
1533                        if (sscanf(line, "%lg", &dval) == 1) {
1534                            int nindex = iz*nx*ny + iy*nx + ix;
1535                            field.define(nindex, dval);
1536                            nread++;
1537                        }
1538                    }
1539                }
1540            }
1541
1542            // make sure that we read all of the expected points
1543            if (nread != nx*ny*nz) {
1544                char mesg[256];
1545                sprintf(mesg,"inconsistent data: expected %d points but found %d points", nx*ny*nz, nread);
1546                result.error(mesg);
1547                return result;
1548            }
1549
1550            // figure out a good mesh spacing
1551            int nsample = 30;
1552            dx = field.rangeMax(Rappture::xaxis) - field.rangeMin(Rappture::xaxis);
1553            dy = field.rangeMax(Rappture::yaxis) - field.rangeMin(Rappture::yaxis);
1554            dz = field.rangeMax(Rappture::zaxis) - field.rangeMin(Rappture::zaxis);
1555            double dmin = pow((dx*dy*dz)/(nsample*nsample*nsample), 0.333);
1556
1557            nx = (int)ceil(dx/dmin);
1558            ny = (int)ceil(dy/dmin);
1559            nz = (int)ceil(dz/dmin);
1560
1561#ifndef NV40
1562            // must be an even power of 2 for older cards
1563                nx = (int)pow(2.0, ceil(log10((double)nx)/log10(2.0)));
1564                ny = (int)pow(2.0, ceil(log10((double)ny)/log10(2.0)));
1565                nz = (int)pow(2.0, ceil(log10((double)nz)/log10(2.0)));
1566#endif
1567
1568            float *data = new float[4*nx*ny*nz];
1569
1570            double vmin = field.valueMin();
1571            double dv = field.valueMax() - field.valueMin();
1572            if (dv == 0.0) { dv = 1.0; }
1573
1574            // generate the uniformly sampled data that we need for a volume
1575            int ngen = 0;
1576            for (int iz=0; iz < nz; iz++) {
1577                double zval = z0 + iz*dmin;
1578                for (int iy=0; iy < ny; iy++) {
1579                    double yval = y0 + iy*dmin;
1580                    for (int ix=0; ix < nx; ix++) {
1581                        double xval = x0 + ix*dmin;
1582                        double v = field.value(xval,yval,zval);
1583
1584                        // scale all values [0-1], -1 => out of bounds
1585                        v = (isnan(v)) ? -1.0 : (v - vmin)/dv;
1586                        data[ngen] = v;
1587                        ngen += 4;
1588                    }
1589                }
1590            }
1591
1592            // Compute the gradient of this data.  BE CAREFUL: center
1593            // calculation on each node to avoid skew in either direction.
1594            ngen = 0;
1595            for (int iz=0; iz < nz; iz++) {
1596                for (int iy=0; iy < ny; iy++) {
1597                    for (int ix=0; ix < nx; ix++) {
1598                        // gradient in x-direction
1599                        double valm1 = (ix == 0) ? 0.0 : data[ngen-1];
1600                        double valp1 = (ix == nx-1) ? 0.0 : data[ngen+1];
1601                        if (valm1 < 0 || valp1 < 0) {
1602                            data[ngen+1] = 0.0;
1603                        } else {
1604                            data[ngen+1] = valp1-valm1; // assume dx=1
1605                        }
1606
1607                        // gradient in y-direction
1608                        valm1 = (iy == 0) ? 0.0 : data[ngen-4*nx];
1609                        valp1 = (iy == ny-1) ? 0.0 : data[ngen+4*nx];
1610                        if (valm1 < 0 || valp1 < 0) {
1611                            data[ngen+2] = 0.0;
1612                        } else {
1613                            data[ngen+2] = valp1-valm1; // assume dy=1
1614                        }
1615
1616                        // gradient in z-direction
1617                        valm1 = (iz == 0) ? 0.0 : data[ngen-4*nx*ny];
1618                        valp1 = (iz == nz-1) ? 0.0 : data[ngen+4*nx*ny];
1619                        if (valm1 < 0 || valp1 < 0) {
1620                            data[ngen+3] = 0.0;
1621                        } else {
1622                            data[ngen+3] = valp1-valm1; // assume dz=1
1623                        }
1624
1625                        ngen += 4;
1626                    }
1627                }
1628            }
1629
1630            load_volume(index, nx, ny, nz, 4, data,
1631                field.valueMin(), field.valueMax());
1632
1633            delete [] data;
1634
1635        } else {
1636            Rappture::Mesh1D zgrid(z0, z0+nz*dz, nz);
1637            Rappture::FieldPrism3D field(xymesh, zgrid);
1638
1639            double dval;
1640            int nread = 0;
1641            while (!fin.eof() && nread < npts) {
1642                if (!(fin >> dval).fail()) {
1643                    field.define(nread++, dval);
1644                }
1645            }
1646
1647            // make sure that we read all of the expected points
1648            if (nread != nxy*nz) {
1649                char mesg[256];
1650                sprintf(mesg,"inconsistent data: expected %d points but found %d points", nxy*nz, nread);
1651                return result.error(mesg);
1652            }
1653
1654            // figure out a good mesh spacing
1655            int nsample = 30;
1656            x0 = field.rangeMin(Rappture::xaxis);
1657            dx = field.rangeMax(Rappture::xaxis) - field.rangeMin(Rappture::xaxis);
1658            y0 = field.rangeMin(Rappture::yaxis);
1659            dy = field.rangeMax(Rappture::yaxis) - field.rangeMin(Rappture::yaxis);
1660            z0 = field.rangeMin(Rappture::zaxis);
1661            dz = field.rangeMax(Rappture::zaxis) - field.rangeMin(Rappture::zaxis);
1662            double dmin = pow((dx*dy*dz)/(nsample*nsample*nsample), 0.333);
1663
1664            nx = (int)ceil(dx/dmin);
1665            ny = (int)ceil(dy/dmin);
1666            nz = (int)ceil(dz/dmin);
1667#ifndef NV40
1668            // must be an even power of 2 for older cards
1669                nx = (int)pow(2.0, ceil(log10((double)nx)/log10(2.0)));
1670                ny = (int)pow(2.0, ceil(log10((double)ny)/log10(2.0)));
1671                nz = (int)pow(2.0, ceil(log10((double)nz)/log10(2.0)));
1672#endif
1673            float *data = new float[4*nx*ny*nz];
1674
1675            double vmin = field.valueMin();
1676            double dv = field.valueMax() - field.valueMin();
1677            if (dv == 0.0) { dv = 1.0; }
1678
1679            // generate the uniformly sampled data that we need for a volume
1680            int ngen = 0;
1681            for (int iz=0; iz < nz; iz++) {
1682                double zval = z0 + iz*dmin;
1683                for (int iy=0; iy < ny; iy++) {
1684                    double yval = y0 + iy*dmin;
1685                    for (int ix=0; ix < nx; ix++) {
1686                        double xval = x0 + ix*dmin;
1687                        double v = field.value(xval,yval,zval);
1688
1689                        // scale all values [0-1], -1 => out of bounds
1690                        v = (isnan(v)) ? -1.0 : (v - vmin)/dv;
1691                        data[ngen] = v;
1692
1693                        ngen += 4;
1694                    }
1695                }
1696            }
1697
1698            // Compute the gradient of this data.  BE CAREFUL: center
1699            // calculation on each node to avoid skew in either direction.
1700            ngen = 0;
1701            for (int iz=0; iz < nz; iz++) {
1702                for (int iy=0; iy < ny; iy++) {
1703                    for (int ix=0; ix < nx; ix++) {
1704                        // gradient in x-direction
1705                        double valm1 = (ix == 0) ? 0.0 : data[ngen-1];
1706                        double valp1 = (ix == nx-1) ? 0.0 : data[ngen+1];
1707                        if (valm1 < 0 || valp1 < 0) {
1708                            data[ngen+1] = 0.0;
1709                        } else {
1710                            data[ngen+1] = valp1-valm1; // assume dx=1
1711                        }
1712
1713                        // gradient in y-direction
1714                        valm1 = (iy == 0) ? 0.0 : data[ngen-4*nx];
1715                        valp1 = (iy == ny-1) ? 0.0 : data[ngen+4*nx];
1716                        if (valm1 < 0 || valp1 < 0) {
1717                            data[ngen+2] = 0.0;
1718                        } else {
1719                            data[ngen+2] = valp1-valm1; // assume dy=1
1720                        }
1721
1722                        // gradient in z-direction
1723                        valm1 = (iz == 0) ? 0.0 : data[ngen-4*nx*ny];
1724                        valp1 = (iz == nz-1) ? 0.0 : data[ngen+4*nx*ny];
1725                        if (valm1 < 0 || valp1 < 0) {
1726                            data[ngen+3] = 0.0;
1727                        } else {
1728                            data[ngen+3] = valp1-valm1; // assume dz=1
1729                        }
1730
1731                        ngen += 4;
1732                    }
1733                }
1734            }
1735
1736            load_volume(index, nx, ny, nz, 4, data,
1737                field.valueMin(), field.valueMax());
1738
1739            delete [] data;
1740        }
1741    } else {
1742        char mesg[256];
1743        sprintf(mesg,"data not found in file %s", fname);
1744        return result.error(mesg);
1745    }
1746
1747    //
1748    // Center this new volume on the origin.
1749    //
1750    float dx0 = -0.5;
1751    float dy0 = -0.5*dy/dx;
1752    float dz0 = -0.5*dz/dx;
1753    volume[index]->move(Vector3(dx0, dy0, dz0));
1754
1755    return result;
1756}
1757
1758/* Load a 3D volume
1759 * index: the index into the volume array, which stores pointers to 3D volume instances
1760 * data: pointer to an array of floats. 
1761 * n_component: the number of scalars for each space point.
1762 *              All component scalars for a point are placed consequtively in data array
1763 * width, height and depth: number of points in each dimension
1764 */
1765void load_volume(int index, int width, int height, int depth,
1766    int n_component, float* data, double vmin, double vmax)
1767{
1768    while (n_volumes <= index) {
1769        volume.push_back(NULL);
1770        n_volumes++;
1771    }
1772
1773    if (volume[index] != NULL) {
1774        delete volume[index];
1775        volume[index] = NULL;
1776    }
1777
1778    volume[index] = new Volume(0.f, 0.f, 0.f, width, height, depth, 1.,
1779                                 n_component, data, vmin, vmax);
1780    assert(volume[index]!=0);
1781}
1782
1783
1784// get a colormap 1D texture by name
1785TransferFunction*
1786get_transfunc(char *name) {
1787    Tcl_HashEntry *entryPtr;
1788    entryPtr = Tcl_FindHashEntry(&tftable, name);
1789    if (entryPtr) {
1790        return (TransferFunction*)Tcl_GetHashValue(entryPtr);
1791    }
1792    return NULL;
1793}
1794
1795
1796//Update the transfer function using local GUI in the non-server mode
1797extern void update_tf_texture(){
1798  glutSetWindow(render_window);
1799
1800  //fprintf(stderr, "tf update\n");
1801  TransferFunction *tf = get_transfunc("default");
1802  if (tf == NULL) return;
1803
1804  float data[256*4];
1805  for(int i=0; i<256; i++){
1806    data[4*i+0] = color_table[i][0];
1807    data[4*i+1] = color_table[i][1];
1808    data[4*i+2] = color_table[i][2];
1809    data[4*i+3] = color_table[i][3];
1810    //fprintf(stderr, "(%f,%f,%f,%f) ", data[4*i+0], data[4*i+1], data[4*i+2], data[4*i+3]);
1811  }
1812
1813  tf->update(data);
1814
1815#ifdef EVENTLOG
1816  float param[3] = {0,0,0};
1817  Event* tmp = new Event(EVENT_ROTATE, param, get_time_interval());
1818  tmp->write(event_log);
1819  delete tmp;
1820#endif
1821}
1822
1823
1824//initialize frame buffer objects for offscreen rendering
1825void init_offscreen_buffer(){
1826
1827  //initialize final fbo for final display
1828  glGenFramebuffersEXT(1, &final_fbo);
1829  glGenTextures(1, &final_color_tex);
1830  glGenRenderbuffersEXT(1, &final_depth_rb);
1831
1832  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
1833
1834  //initialize final color texture
1835  glBindTexture(GL_TEXTURE_2D, final_color_tex);
1836  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1837  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1838#ifdef NV40
1839  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, win_width, win_height, 0,
1840               GL_RGB, GL_INT, NULL);
1841#else
1842  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, win_width, win_height, 0,
1843               GL_RGB, GL_INT, NULL);
1844#endif
1845  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
1846                            GL_COLOR_ATTACHMENT0_EXT,
1847                            GL_TEXTURE_2D, final_color_tex, 0);
1848
1849       
1850  // initialize final depth renderbuffer
1851  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, final_depth_rb);
1852  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
1853                           GL_DEPTH_COMPONENT24, win_width, win_height);
1854  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
1855                               GL_DEPTH_ATTACHMENT_EXT,
1856                               GL_RENDERBUFFER_EXT, final_depth_rb);
1857
1858  // Check framebuffer completeness at the end of initialization.
1859  CHECK_FRAMEBUFFER_STATUS();
1860  assert(glGetError()==0);
1861}
1862
1863
1864//resize the offscreen buffer
1865void resize_offscreen_buffer(int w, int h){
1866  win_width = w;
1867  win_height = h;
1868
1869  //fprintf(stderr, "screen_buffer size: %d\n", sizeof(screen_buffer));
1870
1871  if (screen_buffer) {
1872      delete[] screen_buffer;
1873      screen_buffer = NULL;
1874  }
1875  screen_buffer = new unsigned char[4*win_width*win_height];
1876  assert(screen_buffer != NULL);
1877
1878  //delete the current render buffer resources
1879  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
1880  glDeleteTextures(1, &final_color_tex);
1881  glDeleteFramebuffersEXT(1, &final_fbo);
1882
1883  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, final_depth_rb);
1884  glDeleteRenderbuffersEXT(1, &final_depth_rb);
1885fprintf(stdin,"  after glDeleteRenderbuffers\n");
1886
1887  //change the camera setting
1888  cam->set_screen_size(win_width, win_height);
1889  plane_render->set_screen_size(win_width, win_height);
1890
1891  //Reinitialize final fbo for final display
1892  glGenFramebuffersEXT(1, &final_fbo);
1893  glGenTextures(1, &final_color_tex);
1894  glGenRenderbuffersEXT(1, &final_depth_rb);
1895fprintf(stdin,"  after glGenRenderbuffers\n");
1896
1897  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
1898
1899  //initialize final color texture
1900  glBindTexture(GL_TEXTURE_2D, final_color_tex);
1901  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1902  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1903#ifdef NV40
1904  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, win_width, win_height, 0,
1905               GL_RGB, GL_INT, NULL);
1906#else
1907  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, win_width, win_height, 0,
1908               GL_RGB, GL_INT, NULL);
1909#endif
1910  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
1911                            GL_COLOR_ATTACHMENT0_EXT,
1912                            GL_TEXTURE_2D, final_color_tex, 0);
1913fprintf(stdin,"  after glFramebufferTexture2DEXT\n");
1914       
1915  // initialize final depth renderbuffer
1916  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, final_depth_rb);
1917  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
1918                           GL_DEPTH_COMPONENT24, win_width, win_height);
1919  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
1920                               GL_DEPTH_ATTACHMENT_EXT,
1921                               GL_RENDERBUFFER_EXT, final_depth_rb);
1922fprintf(stdin,"  after glFramebufferRenderEXT\n");
1923
1924  // Check framebuffer completeness at the end of initialization.
1925  CHECK_FRAMEBUFFER_STATUS();
1926  assert(glGetError()==0);
1927fprintf(stdin,"  after assert\n");
1928}
1929
1930
1931void init_cg(){
1932    cgSetErrorCallback(cgErrorCallback);
1933    g_context = cgCreateContext();
1934
1935#ifdef NEW_CG
1936    m_posvel_fprog = loadProgram(g_context, CG_PROFILE_FP40, CG_SOURCE, "./shaders/update_pos_vel.cg");
1937    m_posvel_timestep_param  = cgGetNamedParameter(m_posvel_fprog, "timestep");
1938    m_posvel_damping_param   = cgGetNamedParameter(m_posvel_fprog, "damping");
1939    m_posvel_gravity_param   = cgGetNamedParameter(m_posvel_fprog, "gravity");
1940    m_posvel_spherePos_param = cgGetNamedParameter(m_posvel_fprog, "spherePos");
1941    m_posvel_sphereVel_param = cgGetNamedParameter(m_posvel_fprog, "sphereVel");
1942#endif
1943}
1944
1945
1946//switch shader to change render mode
1947void switch_shader(int choice){
1948
1949  switch (choice){
1950    case 0:
1951      break;
1952
1953   case 1:
1954      break;
1955     
1956   default:
1957      break;
1958  }
1959}
1960
1961void init_particles(){
1962  //random placement on a slice
1963  float* data = new float[psys->psys_width * psys->psys_height * 4];
1964  bzero(data, sizeof(float)*4* psys->psys_width * psys->psys_height);
1965
1966  for (int i=0; i<psys->psys_width; i++){
1967    for (int j=0; j<psys->psys_height; j++){
1968      int index = i + psys->psys_height*j;
1969      bool particle = rand() % 256 > 150;
1970      //particle = true;
1971      if(particle) /*&& i/float(psys->psys_width)>0.3 && i/float(psys->psys_width)<0.7
1972                      && j/float(psys->psys_height)>0.1 && j/float(psys->psys_height)<0.4)*/
1973      {
1974        //assign any location (x,y,z) in range [0,1]
1975        data[4*index] = lic_slice_x;
1976        data[4*index+1]= j/float(psys->psys_height);
1977        data[4*index+2]= i/float(psys->psys_width);
1978        data[4*index+3]= 30; //shorter life span, quicker iterations   
1979      }
1980      else
1981      {
1982        data[4*index] = 0;
1983        data[4*index+1]= 0;
1984        data[4*index+2]= 0;
1985        data[4*index+3]= 0;     
1986      }
1987    }
1988   }
1989
1990  psys->initialize((Particle*)data);
1991  delete[] data;
1992}
1993
1994
1995//init line integral convolution
1996void init_lic(){
1997  lic = new Lic(NMESH, win_width, win_height, 0.3, g_context, volume[1]->id,
1998                  volume[1]->aspect_ratio_width,
1999                  volume[1]->aspect_ratio_height,
2000                  volume[1]->aspect_ratio_depth);
2001}
2002
2003//init the particle system using vector field volume->[1]
2004void init_particle_system(){
2005   psys = new ParticleSystem(NMESH, NMESH, g_context, volume[1]->id,
2006                   1./volume[1]->aspect_ratio_width,
2007                   1./volume[1]->aspect_ratio_height,
2008                   1./volume[1]->aspect_ratio_depth);
2009
2010   init_particles();    //fill initial particles
2011}
2012
2013
2014void make_test_2D_data(){
2015
2016  int w = 300;
2017  int h = 200;
2018  float* data = new float[w*h];
2019
2020  //procedurally make a gradient plane
2021  for(int j=0; j<h; j++){
2022    for(int i=0; i<w; i++){
2023      data[w*j+i] = float(i)/float(w);
2024    }
2025  }
2026
2027  plane[0] = new Texture2D(w, h, GL_FLOAT, GL_LINEAR, 1, data);
2028
2029  delete[] data;
2030}
2031
2032
2033/*----------------------------------------------------*/
2034void initGL(void)
2035{
2036   system_info();
2037   init_glew();
2038
2039   //buffer to store data read from the screen
2040   if (screen_buffer) {
2041       delete[] screen_buffer;
2042       screen_buffer = NULL;
2043   }
2044   screen_buffer = new unsigned char[4*win_width*win_height];
2045   assert(screen_buffer != NULL);
2046
2047   //create the camera with default setting
2048   cam = new Camera(win_width, win_height,
2049                   live_obj_x, live_obj_y, live_obj_z,
2050                   0., 0., 100.,
2051                   (int)live_rot_x, (int)live_rot_y, (int)live_rot_z);
2052
2053   glEnable(GL_TEXTURE_2D);
2054   glShadeModel(GL_FLAT);
2055   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2056   glClearColor(0,0,0,1);
2057   glClear(GL_COLOR_BUFFER_BIT);
2058
2059   //initialize lighting
2060   GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
2061   GLfloat mat_shininess[] = {30.0};
2062   GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};
2063   GLfloat green_light[] = {0.1, 0.5, 0.1, 1.0};
2064
2065   glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
2066   glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
2067   glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
2068   glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);     
2069   glLightfv(GL_LIGHT1, GL_DIFFUSE, green_light);
2070   glLightfv(GL_LIGHT1, GL_SPECULAR, white_light);     
2071
2072   // init table of transfer functions
2073   Tcl_InitHashTable(&tftable, TCL_STRING_KEYS);
2074
2075   //check if performance query is supported
2076   if(check_query_support()){
2077     //create queries to count number of rendered pixels
2078     perf = new PerfQuery();
2079   }
2080
2081   //load_volume_file(0, "./data/A-apbs-2-out-potential-PE0.dx");
2082   //load_volume_file(0, "./data/nw-AB-Vg=0.000-Vd=1.000-potential.dx");
2083   //load_volume_file(0, "./data/test2.dx");
2084   //load_volume_file(0, "./data/mu-wire-3d.dx"); //I added this line to debug: Wei
2085   //load_volume_file(0, "./data/input_nd_dx_4"); //take a VERY long time?
2086   //load_vector_file(1, "./data/J-wire-vec.dx");
2087   //load_volume_file(1, "./data/mu-wire-3d.dx");  //I added this line to debug: Wei
2088   //load_volume_file(3, "./data/mu-wire-3d.dx");
2089   //load_volume_file(4, "./data/mu-wire-3d.dx");
2090
2091   init_offscreen_buffer();    //frame buffer object for offscreen rendering
2092   init_cg();   //create cg shader context
2093
2094   //create volume renderer and add volumes to it
2095   vol_render = new VolumeRenderer(g_context);
2096
2097   
2098   /*
2099   //I added this to debug : Wei
2100   float tmp_data[4*124];
2101   memset(tmp_data, 0, 4*4*124);
2102   TransferFunction* tmp_tf = new TransferFunction(124, tmp_data);
2103   vol_render->add_volume(volume[0], tmp_tf);
2104   volume[0]->get_cutplane(0)->enabled = false;
2105   volume[0]->get_cutplane(1)->enabled = false;
2106   volume[0]->get_cutplane(2)->enabled = false;
2107
2108   //volume[1]->move(Vector3(0.5, 0.6, 0.7));
2109   //vol_render->add_volume(volume[1], tmp_tf);
2110   */
2111
2112
2113   //create an 2D plane renderer
2114   plane_render = new PlaneRenderer(g_context, win_width, win_height);
2115   make_test_2D_data();
2116
2117   plane_render->add_plane(plane[0], get_transfunc("default"));
2118
2119   assert(glGetError()==0);
2120
2121   //init_particle_system();
2122   //init_lic();
2123}
2124
2125
2126
2127void initTcl(){
2128    interp = Tcl_CreateInterp();
2129    Tcl_MakeSafe(interp);
2130
2131    Tcl_DStringInit(&cmdbuffer);
2132
2133    // manipulate the viewpoint
2134    Tcl_CreateCommand(interp, "camera", CameraCmd,
2135        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2136
2137    // turn on/off cut planes in x/y/z directions
2138    Tcl_CreateCommand(interp, "cutplane", CutplaneCmd,
2139        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2140
2141    // request the legend for a plot (transfer function)
2142    Tcl_CreateCommand(interp, "legend", LegendCmd,
2143        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2144
2145    // change the size of the screen (size of picture generated)
2146    Tcl_CreateCommand(interp, "screen", ScreenCmd,
2147        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2148
2149    // manipulate transfer functions
2150    Tcl_CreateCommand(interp, "transfunc", TransfuncCmd,
2151        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2152
2153    // set the "up" direction for volumes
2154    Tcl_CreateCommand(interp, "up", UpCmd,
2155        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2156
2157    // manipulate volume data
2158    Tcl_CreateCommand(interp, "volume", VolumeCmd,
2159        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
2160
2161    // create a default transfer function
2162    if (Tcl_Eval(interp, def_transfunc) != TCL_OK) {
2163        fprintf(stdin, "WARNING: bad default transfer function\n");
2164        fprintf(stdin, Tcl_GetStringResult(interp));
2165    }
2166}
2167
2168
2169void read_screen(){
2170  //glBindTexture(GL_TEXTURE_2D, 0);
2171  //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
2172 
2173  glReadPixels(0, 0, win_width, win_height, GL_RGB, GL_UNSIGNED_BYTE, screen_buffer);
2174  assert(glGetError()==0);
2175}
2176
2177void display();
2178
2179
2180#if DO_RLE
2181char rle[512*512*3];
2182int rle_size;
2183
2184short offsets[512*512*3];
2185int offsets_size;
2186
2187void do_rle(){
2188  int len = win_width*win_height*3;
2189  rle_size = 0;
2190  offsets_size = 0;
2191
2192  int i=0;
2193  while(i<len){
2194    if (screen_buffer[i] == 0) {
2195      int pos = i+1;
2196      while ( (pos<len) && (screen_buffer[pos] == 0)) {
2197        pos++;
2198      }
2199      offsets[offsets_size++] = -(pos - i);
2200      i = pos;
2201    }
2202
2203    else {
2204      int pos;
2205      for (pos = i; (pos<len) && (screen_buffer[pos] != 0); pos++) {
2206        rle[rle_size++] = screen_buffer[pos];
2207      }
2208      offsets[offsets_size++] = (pos - i);
2209      i = pos;
2210    }
2211
2212  }
2213}
2214#endif
2215
2216// used internally to build up the BMP file header
2217// writes an integer value into the header data structure at pos
2218void
2219bmp_header_add_int(unsigned char* header, int& pos, int data)
2220{
2221    header[pos++] = data & 0xff;
2222    header[pos++] = (data >> 8) & 0xff;
2223    header[pos++] = (data >> 16) & 0xff;
2224    header[pos++] = (data >> 24) & 0xff;
2225}
2226
2227void
2228bmp_write(const char* cmd)
2229{
2230    unsigned char header[54];
2231    int pos = 0;
2232    header[pos++] = 'B';
2233    header[pos++] = 'M';
2234
2235    // BE CAREFUL:  BMP files must have an even multiple of 4 bytes
2236    // on each scan line.  If need be, we add padding to each line.
2237    int pad = 0;
2238    if ((3*win_width) % 4 > 0) {
2239        pad = 4 - ((3*win_width) % 4);
2240    }
2241
2242    // file size in bytes
2243    int fsize = (3*win_width+pad)*win_height + sizeof(header);
2244    bmp_header_add_int(header, pos, fsize);
2245
2246    // reserved value (must be 0)
2247    bmp_header_add_int(header, pos, 0);
2248
2249    // offset in bytes to start of bitmap data
2250    bmp_header_add_int(header, pos, sizeof(header));
2251
2252    // size of the BITMAPINFOHEADER
2253    bmp_header_add_int(header, pos, 40);
2254
2255    // width of the image in pixels
2256    bmp_header_add_int(header, pos, win_width);
2257
2258    // height of the image in pixels
2259    bmp_header_add_int(header, pos, win_height);
2260
2261    // 1 plane + (24 bits/pixel << 16)
2262    bmp_header_add_int(header, pos, 1572865);
2263
2264    // no compression
2265    // size of image for compression
2266    bmp_header_add_int(header, pos, 0);
2267    bmp_header_add_int(header, pos, 0);
2268
2269    // x pixels per meter
2270    // y pixels per meter
2271    bmp_header_add_int(header, pos, 0);
2272    bmp_header_add_int(header, pos, 0);
2273
2274    // number of colors used (0 = compute from bits/pixel)
2275    // number of important colors (0 = all colors important)
2276    bmp_header_add_int(header, pos, 0);
2277    bmp_header_add_int(header, pos, 0);
2278
2279    // BE CAREFUL: BMP format wants BGR ordering for screen data
2280    unsigned char* scr = screen_buffer;
2281    for (int row=0; row < win_height; row++) {
2282        for (int col=0; col < win_width; col++) {
2283            unsigned char tmp = scr[2];
2284            scr[2] = scr[0];  // B
2285            scr[0] = tmp;     // R
2286            scr += 3;
2287        }
2288        scr += pad;  // skip over padding already in screen data
2289    }
2290
2291    std::ostringstream result;
2292    result << cmd << " " << fsize << "\n";
2293    write(0, result.str().c_str(), result.str().size());
2294
2295    write(0, header, sizeof(header));
2296    write(0, screen_buffer, (3*win_width+pad)*win_height);
2297}
2298
2299
2300void xinetd_listen(){
2301
2302    int flags = fcntl(0, F_GETFL, 0);
2303    fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
2304
2305    int status = TCL_OK;
2306    int npass = 0;
2307
2308    //
2309    //  Read and execute as many commands as we can from stdin...
2310    //
2311    while (status == TCL_OK) {
2312        //
2313        //  Read the next command from the buffer.  First time through
2314        //  we block here and wait if necessary until a command comes in.
2315        //
2316        //  BE CAREFUL:  Read only one command, up to a newline.
2317        //  The "volume data follows" command needs to be able to read
2318        //  the data immediately following the command, and we shouldn't
2319        //  consume it here.
2320        //
2321        while (1) {
2322            char c = getchar();
2323            if (c <= 0) {
2324                if (npass == 0) {
2325                    exit(0);  // EOF -- we're done!
2326                } else {
2327                    break;
2328                }
2329            }
2330            Tcl_DStringAppend(&cmdbuffer, &c, 1);
2331
2332            if (c=='\n' && Tcl_CommandComplete(Tcl_DStringValue(&cmdbuffer))) {
2333                break;
2334            }
2335        }
2336
2337        // no command? then we're done for now
2338        if (Tcl_DStringLength(&cmdbuffer) == 0) {
2339            break;
2340        }
2341
2342        // back to original flags during command evaluation...
2343        fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
2344
2345        status = Tcl_Eval(interp, Tcl_DStringValue(&cmdbuffer));
2346        Tcl_DStringSetLength(&cmdbuffer, 0);
2347
2348        // non-blocking for next read -- we might not get anything
2349        fcntl(0, F_SETFL, flags | O_NONBLOCK);
2350        npass++;
2351    }
2352    fcntl(0, F_SETFL, flags);
2353
2354    if (status != TCL_OK) {
2355        std::ostringstream errmsg;
2356        errmsg << "ERROR: " << Tcl_GetStringResult(interp) << std::endl;
2357        write(0, errmsg.str().c_str(), errmsg.str().size());
2358        return;
2359    }
2360
2361    //
2362    //  Generate the latest frame and send it back to the client
2363    //
2364    display();
2365
2366#if DO_RLE
2367    do_rle();
2368    int sizes[2] = {  offsets_size*sizeof(offsets[0]), rle_size };
2369    fprintf(stderr, "Writing %d,%d\n", sizes[0], sizes[1]); fflush(stderr);
2370    write(0, &sizes, sizeof(sizes));
2371    write(0, offsets, offsets_size*sizeof(offsets[0]));
2372    write(0, rle, rle_size);    //unsigned byte
2373#else
2374    bmp_write("nv>image -bytes");
2375#endif
2376}
2377
2378
2379/*
2380//draw vectors
2381void draw_arrows(){
2382  glColor4f(0.,0.,1.,1.);
2383  for(int i=0; i<NMESH; i++){
2384    for(int j=0; j<NMESH; j++){
2385      Vector2 v = grid.get(i, j);
2386
2387      int x1 = i*DM;
2388      int y1 = j*DM;
2389
2390      int x2 = x1 + v.x;
2391      int y2 = y1 + v.y;
2392
2393      glBegin(GL_LINES);
2394        glVertex2d(x1, y1);
2395        glVertex2d(x2, y2);
2396      glEnd();
2397    }
2398  }
2399}
2400*/
2401
2402
2403/*----------------------------------------------------*/
2404void idle(){
2405  glutSetWindow(render_window);
2406
2407 
2408  /*
2409  struct timespec ts;
2410  ts.tv_sec = 0;
2411  ts.tv_nsec = 300000000;
2412  nanosleep(&ts, 0);
2413  */
2414 
2415
2416#ifdef XINETD
2417  xinetd_listen();
2418#else
2419  glutPostRedisplay();
2420#endif
2421}
2422
2423
2424void display_offscreen_buffer(){
2425   glEnable(GL_TEXTURE_2D);
2426   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
2427   glBindTexture(GL_TEXTURE_2D, final_color_tex);
2428
2429   glViewport(0, 0, win_width, win_height);
2430   glMatrixMode(GL_PROJECTION);
2431   glLoadIdentity();
2432   gluOrtho2D(0, win_width, 0, win_height);
2433   glMatrixMode(GL_MODELVIEW);
2434   glLoadIdentity();
2435
2436   glColor3f(1.,1.,1.);         //MUST HAVE THIS LINE!!!
2437   glBegin(GL_QUADS);
2438   glTexCoord2f(0, 0); glVertex2f(0, 0);
2439   glTexCoord2f(1, 0); glVertex2f(win_width, 0);
2440   glTexCoord2f(1, 1); glVertex2f(win_width, win_height);
2441   glTexCoord2f(0, 1); glVertex2f(0, win_height);
2442   glEnd();
2443}
2444
2445void offscreen_buffer_capture(){
2446   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
2447}
2448
2449void draw_bounding_box(float x0, float y0, float z0,
2450                float x1, float y1, float z1,
2451                float r, float g, float b, float line_width)
2452{
2453        glDisable(GL_TEXTURE_2D);
2454
2455        glColor4d(r, g, b, 1.0);
2456        glLineWidth(line_width);
2457       
2458        glBegin(GL_LINE_LOOP);
2459
2460                glVertex3d(x0, y0, z0);
2461                glVertex3d(x1, y0, z0);
2462                glVertex3d(x1, y1, z0);
2463                glVertex3d(x0, y1, z0);
2464               
2465        glEnd();
2466
2467        glBegin(GL_LINE_LOOP);
2468
2469                glVertex3d(x0, y0, z1);
2470                glVertex3d(x1, y0, z1);
2471                glVertex3d(x1, y1, z1);
2472                glVertex3d(x0, y1, z1);
2473               
2474        glEnd();
2475
2476
2477        glBegin(GL_LINE_LOOP);
2478
2479                glVertex3d(x0, y0, z0);
2480                glVertex3d(x0, y0, z1);
2481                glVertex3d(x0, y1, z1);
2482                glVertex3d(x0, y1, z0);
2483               
2484        glEnd();
2485
2486        glBegin(GL_LINE_LOOP);
2487
2488                glVertex3d(x1, y0, z0);
2489                glVertex3d(x1, y0, z1);
2490                glVertex3d(x1, y1, z1);
2491                glVertex3d(x1, y1, z0);
2492               
2493        glEnd();
2494
2495        glEnable(GL_TEXTURE_2D);
2496}
2497
2498
2499
2500int particle_distance_sort(const void* a, const void* b){
2501  if((*((Particle*)a)).aux > (*((Particle*)b)).aux)
2502    return -1;
2503  else
2504    return 1;
2505}
2506
2507
2508void soft_read_verts(){
2509  glReadPixels(0, 0, psys->psys_width, psys->psys_height, GL_RGB, GL_FLOAT, vert);
2510  //fprintf(stderr, "soft_read_vert");
2511
2512  //cpu sort the distance 
2513  Particle* p = (Particle*) malloc(sizeof(Particle)* psys->psys_width * psys->psys_height);
2514  for(int i=0; i<psys->psys_width * psys->psys_height; i++){
2515    float x = vert[3*i];
2516    float y = vert[3*i+1];
2517    float z = vert[3*i+2];
2518
2519    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);
2520    p[i].x = x;
2521    p[i].y = y;
2522    p[i].z = z;
2523    p[i].aux = dis;
2524  }
2525
2526  qsort(p, psys->psys_width * psys->psys_height, sizeof(Particle), particle_distance_sort);
2527
2528  for(int i=0; i<psys->psys_width * psys->psys_height; i++){
2529    vert[3*i] = p[i].x;
2530    vert[3*i+1] = p[i].y;
2531    vert[3*i+2] = p[i].z;
2532  }
2533
2534  free(p);
2535}
2536
2537
2538//display the content of a texture as a screen aligned quad
2539void display_texture(NVISid tex, int width, int height){
2540   glPushMatrix();
2541
2542   glEnable(GL_TEXTURE_2D);
2543   glBindTexture(GL_TEXTURE_RECTANGLE_NV, tex);
2544
2545   glViewport(0, 0, width, height);
2546   glMatrixMode(GL_PROJECTION);
2547   glLoadIdentity();
2548   gluOrtho2D(0, width, 0, height);
2549   glMatrixMode(GL_MODELVIEW);
2550   glLoadIdentity();
2551
2552   cgGLBindProgram(m_passthru_fprog);
2553   cgGLEnableProfile(CG_PROFILE_FP30);
2554
2555   cgGLSetParameter4f(m_passthru_scale_param, 1.0, 1.0, 1.0, 1.0);
2556   cgGLSetParameter4f(m_passthru_bias_param, 0.0, 0.0, 0.0, 0.0);
2557   
2558   draw_quad(width, height, width, height);
2559   cgGLDisableProfile(CG_PROFILE_FP30);
2560
2561   glPopMatrix();
2562
2563   assert(glGetError()==0);
2564}
2565
2566
2567//draw vertices in the main memory
2568void soft_display_verts(){
2569  glDisable(GL_TEXTURE_2D);
2570  glEnable(GL_BLEND);
2571
2572  glPointSize(0.5);
2573  glColor4f(0,0.8,0.8,0.6);
2574  glBegin(GL_POINTS);
2575  for(int i=0; i<psys->psys_width * psys->psys_height; i++){
2576    glVertex3f(vert[3*i], vert[3*i+1], vert[3*i+2]);
2577  }
2578  glEnd();
2579  //fprintf(stderr, "soft_display_vert");
2580}
2581
2582
2583#if 0
2584
2585//oddeven sort on GPU
2586void sortstep()
2587{
2588    // perform one step of the current sorting algorithm
2589
2590        /*
2591    // swap buffers
2592    int sourceBuffer = targetBuffer;
2593    targetBuffer = (targetBuffer+1)%2;   
2594    int pstage = (1<<stage);
2595    int ppass  = (1<<pass);
2596    int activeBitonicShader = 0;
2597
2598#ifdef _WIN32
2599    buffer->BindBuffer(wglTargets[sourceBuffer]);
2600#else
2601    buffer->BindBuffer(glTargets[sourceBuffer]);
2602#endif
2603    if (buffer->IsDoubleBuffered()) glDrawBuffer(glTargets[targetBuffer]);
2604    */
2605
2606    checkGLError("after db");
2607
2608    int pstage = (1<<stage);
2609    int ppass  = (1<<pass);
2610    //int activeBitonicShader = 0;
2611
2612    // switch on correct sorting shader
2613    oddevenMergeSort.bind();
2614    glUniform3fARB(oddevenMergeSort.getUniformLocation("Param1"), float(pstage+pstage),
2615                   float(ppass%pstage), float((pstage+pstage)-(ppass%pstage)-1));
2616    glUniform3fARB(oddevenMergeSort.getUniformLocation("Param2"), float(psys_width), float(psys_height), float(ppass));
2617    glUniform1iARB(oddevenMergeSort.getUniformLocation("Data"), 0);
2618    staticdebugmsg("sort","stage "<<pstage<<" pass "<<ppass);
2619
2620    // This clear is not necessary for sort to function. But if we are in interactive mode
2621    // unused parts of the texture that are visible will look bad.
2622    //if (!perfTest) glClear(GL_COLOR_BUFFER_BIT);
2623
2624    //buffer->Bind();
2625    //buffer->EnableTextureTarget();
2626
2627    // initiate the sorting step on the GPU
2628    // a full-screen quad
2629    glBegin(GL_QUADS);
2630    /*
2631    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,0.0f,0.0f,0.0f,1.0f); glVertex2f(-1.0f,-1.0f);
2632    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,float(psys_width),0.0f,1.0f,1.0f); glVertex2f(1.0f,-1.0f);
2633    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,float(psys_width),float(psys_height),1.0f,0.0f); glVertex2f(1.0f,1.0f);       
2634    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,0.0f,float(psys_height),0.0f,0.0f); glVertex2f(-1.0f,1.0f);   
2635    */
2636    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,0.0f,0.0f,0.0f,1.0f); glVertex2f(0.,0.);       
2637    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,float(psys_width),0.0f,1.0f,1.0f); glVertex2f(float(psys_width), 0.);
2638    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,float(psys_width),float(psys_height),1.0f,0.0f); glVertex2f(float(psys_width), float(psys_height));   
2639    glMultiTexCoord4fARB(GL_TEXTURE0_ARB,0.0f,float(psys_height),0.0f,0.0f); glVertex2f(0., float(psys_height));       
2640    glEnd();
2641
2642
2643    // switch off sorting shader
2644    oddevenMergeSort.release();
2645
2646    //buffer->DisableTextureTarget();
2647
2648    assert(glGetError()==0);
2649}
2650
2651#endif
2652
2653
2654void draw_3d_axis(){
2655  glDisable(GL_TEXTURE_2D);
2656  glEnable(GL_DEPTH_TEST);
2657
2658        //draw axes
2659        GLUquadric *obj;
2660        obj = gluNewQuadric();
2661       
2662        glDepthFunc(GL_LESS);
2663        glEnable(GL_DEPTH_TEST);
2664        glDisable(GL_BLEND);
2665
2666        int segments = 50;
2667
2668        glColor3f(0.8, 0.8, 0.8);
2669        glPushMatrix();
2670        glTranslatef(0.4, 0., 0.);
2671        glRotatef(90, 1, 0, 0);
2672        glRotatef(180, 0, 1, 0);
2673        glScalef(0.0005, 0.0005, 0.0005);
2674        glutStrokeCharacter(GLUT_STROKE_ROMAN, 'x');
2675        glPopMatrix(); 
2676
2677        glPushMatrix();
2678        glTranslatef(0., 0.4, 0.);
2679        glRotatef(90, 1, 0, 0);
2680        glRotatef(180, 0, 1, 0);
2681        glScalef(0.0005, 0.0005, 0.0005);
2682        glutStrokeCharacter(GLUT_STROKE_ROMAN, 'y');
2683        glPopMatrix(); 
2684
2685        glPushMatrix();
2686        glTranslatef(0., 0., 0.4);
2687        glRotatef(90, 1, 0, 0);
2688        glRotatef(180, 0, 1, 0);
2689        glScalef(0.0005, 0.0005, 0.0005);
2690        glutStrokeCharacter(GLUT_STROKE_ROMAN, 'z');
2691        glPopMatrix(); 
2692
2693        glEnable(GL_LIGHTING);
2694        glEnable(GL_LIGHT0);
2695
2696        glColor3f(0.2, 0.2, 0.8);
2697        glPushMatrix();
2698        glutSolidSphere(0.02, segments, segments );
2699        glPopMatrix();
2700
2701        glPushMatrix();
2702        glRotatef(-90, 1, 0, 0);       
2703        gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
2704        glPopMatrix(); 
2705
2706        glPushMatrix();
2707        glTranslatef(0., 0.3, 0.);
2708        glRotatef(-90, 1, 0, 0);       
2709        gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
2710        glPopMatrix(); 
2711
2712        glPushMatrix();
2713        glRotatef(90, 0, 1, 0);
2714        gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
2715        glPopMatrix(); 
2716
2717        glPushMatrix();
2718        glTranslatef(0.3, 0., 0.);
2719        glRotatef(90, 0, 1, 0);
2720        gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
2721        glPopMatrix(); 
2722
2723        glPushMatrix();
2724        gluCylinder(obj, 0.01, 0.01, 0.3, segments, segments);
2725        glPopMatrix(); 
2726
2727        glPushMatrix();
2728        glTranslatef(0., 0., 0.3);
2729        gluCylinder(obj, 0.02, 0.0, 0.06, segments, segments);
2730        glPopMatrix(); 
2731
2732        glDisable(GL_LIGHTING);
2733        glDisable(GL_DEPTH_TEST);
2734        gluDeleteQuadric(obj);
2735
2736  glEnable(GL_TEXTURE_2D);
2737  glDisable(GL_DEPTH_TEST);
2738}
2739
2740
2741void draw_axis(){
2742
2743  glDisable(GL_TEXTURE_2D);
2744  glEnable(GL_DEPTH_TEST);
2745
2746  //red x
2747  glColor3f(1,0,0);
2748  glBegin(GL_LINES);
2749    glVertex3f(0,0,0);
2750    glVertex3f(1.5,0,0);
2751  glEnd();
2752
2753  //blue y
2754  glColor3f(0,0,1);
2755  glBegin(GL_LINES);
2756    glVertex3f(0,0,0);
2757    glVertex3f(0,1.5,0);
2758  glEnd();
2759
2760  //green z
2761  glColor3f(0,1,0);
2762  glBegin(GL_LINES);
2763    glVertex3f(0,0,0);
2764    glVertex3f(0,0,1.5);
2765  glEnd();
2766
2767  glEnable(GL_TEXTURE_2D);
2768  glDisable(GL_DEPTH_TEST);
2769}
2770
2771
2772
2773/*----------------------------------------------------*/
2774void display()
2775{
2776
2777   assert(glGetError()==0);
2778
2779   //lic->convolve(); //flow line integral convolution
2780   //psys->advect(); //advect particles
2781
2782   offscreen_buffer_capture();  //enable offscreen render
2783   //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); //enable onscreen render
2784
2785   //start final rendering
2786   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear screen
2787
2788   if (volume_mode) {
2789     //3D rendering mode
2790     glEnable(GL_TEXTURE_2D);
2791     glEnable(GL_DEPTH_TEST);
2792
2793     //camera setting activated
2794     cam->activate();
2795
2796     //set up the orientation of items in the scene.
2797     glPushMatrix();
2798       switch (updir) {
2799         case 1:  // x
2800               glRotatef(90, 0, 0, 1);
2801               glRotatef(90, 1, 0, 0);
2802           break;
2803
2804         case 2:  // y
2805           // this is the default
2806           break;
2807
2808         case 3:  // z
2809               glRotatef(-90, 1, 0, 0);
2810               glRotatef(-90, 0, 0, 1);
2811           break;
2812
2813         case -1:  // -x
2814               glRotatef(-90, 0, 0, 1);
2815           break;
2816
2817         case -2:  // -y
2818               glRotatef(180, 0, 0, 1);
2819               glRotatef(-90, 0, 1, 0);
2820           break;
2821
2822         case -3:  // -z
2823               glRotatef(90, 1, 0, 0);
2824           break;
2825       }
2826
2827       //now render things in the scene
2828       draw_3d_axis();
2829
2830       //lic->render();         //display the line integral convolution result
2831       //soft_display_verts();
2832       //perf->enable();
2833       //  psys->render();
2834       //perf->disable();
2835       //fprintf(stderr, "particle pixels: %d\n", perf->get_pixel_count());
2836       //perf->reset();
2837
2838       perf->enable();
2839         vol_render->render_all();
2840       perf->disable();
2841
2842     glPopMatrix();
2843   }
2844   else{
2845     //2D rendering mode
2846     perf->enable();
2847       plane_render->render();
2848     perf->disable();
2849   }
2850
2851
2852#ifdef XINETD
2853   float cost  = perf->get_pixel_count();
2854   write(3, &cost, sizeof(cost));
2855#endif
2856   perf->reset();
2857
2858   display_offscreen_buffer(); //display the final rendering on screen
2859
2860#ifdef XINETD
2861   read_screen();
2862#else
2863   //read_screen();
2864#endif   
2865
2866   glutSwapBuffers();
2867}
2868
2869
2870void mouse(int button, int state, int x, int y){
2871  if(button==GLUT_LEFT_BUTTON){
2872
2873    if(state==GLUT_DOWN){
2874      left_last_x = x;
2875      left_last_y = y;
2876      left_down = true;
2877      right_down = false;
2878    }
2879    else{
2880      left_down = false;
2881      right_down = false;
2882    }
2883  }
2884  else{
2885    //fprintf(stderr, "right mouse\n");
2886
2887    if(state==GLUT_DOWN){
2888      //fprintf(stderr, "right mouse down\n");
2889      right_last_x = x;
2890      right_last_y = y;
2891      left_down = false;
2892      right_down = true;
2893    }
2894    else{
2895      //fprintf(stderr, "right mouse up\n");
2896      left_down = false;
2897      right_down = false;
2898    }
2899  }
2900}
2901
2902
2903void update_rot(int delta_x, int delta_y){
2904        live_rot_x += delta_x;
2905        live_rot_y += delta_y;
2906
2907        if(live_rot_x > 360.0)
2908                live_rot_x -= 360.0;   
2909        else if(live_rot_x < -360.0)
2910                live_rot_x += 360.0;
2911
2912        if(live_rot_y > 360.0)
2913                live_rot_y -= 360.0;   
2914        else if(live_rot_y < -360.0)
2915                live_rot_y += 360.0;
2916
2917        cam->rotate(live_rot_x, live_rot_y, live_rot_z);
2918}
2919
2920
2921void update_trans(int delta_x, int delta_y, int delta_z){
2922        live_obj_x += delta_x*0.03;
2923        live_obj_y += delta_y*0.03;
2924        live_obj_z += delta_z*0.03;
2925}
2926
2927void end_service();
2928
2929void keyboard(unsigned char key, int x, int y){
2930 
2931   bool log = false;
2932
2933   switch (key){
2934        case 'q':
2935#ifdef XINETD
2936                end_service();
2937#endif
2938                exit(0);
2939                break;
2940        case '+':
2941                lic_slice_z+=0.05;
2942                lic->set_offset(lic_slice_z);
2943                break;
2944        case '-':
2945                lic_slice_z-=0.05;
2946                lic->set_offset(lic_slice_z);
2947                break;
2948        case ',':
2949                lic_slice_x+=0.05;
2950                init_particles();
2951                break;
2952        case '.':
2953                lic_slice_x-=0.05;
2954                init_particles();
2955                break;
2956        case '1':
2957                advect = true;
2958                break;
2959        case '2':
2960                psys_x+=0.05;
2961                break;
2962        case '3':
2963                psys_x-=0.05;
2964                break;
2965        case 'w': //zoom out
2966                live_obj_z-=0.05;
2967                log = true;
2968                cam->move(live_obj_x, live_obj_y, live_obj_z);
2969                break;
2970        case 's': //zoom in
2971                live_obj_z+=0.05;
2972                log = true;
2973                cam->move(live_obj_x, live_obj_y, live_obj_z);
2974                break;
2975        case 'a': //left
2976                live_obj_x-=0.05;
2977                log = true;
2978                cam->move(live_obj_x, live_obj_y, live_obj_z);
2979                break;
2980        case 'd': //right
2981                live_obj_x+=0.05;
2982                log = true;
2983                cam->move(live_obj_x, live_obj_y, live_obj_z);
2984                break;
2985        case 'i':
2986                init_particles();
2987                break;
2988        case 'v':
2989                vol_render->switch_volume_mode();
2990                break;
2991        case 'b':
2992                vol_render->switch_slice_mode();
2993                break;
2994        case 'n':
2995                resize_offscreen_buffer(win_width*2, win_height*2);
2996                break;
2997        case 'm':
2998                resize_offscreen_buffer(win_width/2, win_height/2);
2999                break;
3000
3001        default:
3002                break;
3003    }   
3004
3005#ifdef EVENTLOG
3006   if(log){
3007     float param[3] = {live_obj_x, live_obj_y, live_obj_z};
3008     Event* tmp = new Event(EVENT_MOVE, param, get_time_interval());
3009     tmp->write(event_log);
3010     delete tmp;
3011   }
3012#endif
3013}
3014
3015void motion(int x, int y){
3016
3017    int old_x, old_y;   
3018
3019    if(left_down){
3020      old_x = left_last_x;
3021      old_y = left_last_y;   
3022    }
3023    else if(right_down){
3024      old_x = right_last_x;
3025      old_y = right_last_y;   
3026    }
3027
3028    int delta_x = x - old_x;
3029    int delta_y = y - old_y;
3030
3031    //more coarse event handling
3032    //if(abs(delta_x)<10 && abs(delta_y)<10)
3033      //return;
3034
3035    if(left_down){
3036      left_last_x = x;
3037      left_last_y = y;
3038
3039      update_rot(-delta_y, -delta_x);
3040    }
3041    else if (right_down){
3042      //fprintf(stderr, "right mouse motion (%d,%d)\n", x, y);
3043
3044      right_last_x = x;
3045      right_last_y = y;
3046
3047      update_trans(0, 0, delta_x);
3048    }
3049
3050#ifdef EVENTLOG
3051    float param[3] = {live_rot_x, live_rot_y, live_rot_z};
3052    Event* tmp = new Event(EVENT_ROTATE, param, get_time_interval());
3053    tmp->write(event_log);
3054    delete tmp;
3055#endif
3056
3057    glutPostRedisplay();
3058}
3059
3060#ifdef XINETD
3061void init_service(){
3062  //open log and map stderr to log file
3063  xinetd_log = fopen("log.txt", "w");
3064  close(2);
3065  dup2(fileno(xinetd_log), 2);
3066  dup2(2,1);
3067
3068  //flush junk
3069  fflush(stdout);
3070  fflush(stderr);
3071}
3072
3073void end_service(){
3074  //close log file
3075  fclose(xinetd_log);
3076}
3077#endif
3078
3079void init_event_log(){
3080  event_log = fopen("event.txt", "w");
3081  assert(event_log!=0);
3082
3083  struct timeval time;
3084  gettimeofday(&time, NULL);
3085  cur_time = time.tv_sec*1000. + time.tv_usec/1000.;
3086}
3087
3088void end_event_log(){
3089  fclose(event_log);
3090}
3091
3092double get_time_interval(){
3093  struct timeval time;
3094  gettimeofday(&time, NULL);
3095  double new_time = time.tv_sec*1000. + time.tv_usec/1000.;
3096
3097  double interval = new_time - cur_time;
3098  cur_time = new_time;
3099  return interval;
3100}
3101
3102
3103/*----------------------------------------------------*/
3104int main(int argc, char** argv)
3105{
3106#ifdef XINETD
3107   init_service();
3108#endif
3109
3110   glutInit(&argc, argv);
3111   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
3112   
3113   glutInitWindowSize(win_width, win_height);
3114   glutInitWindowPosition(10, 10);
3115   render_window = glutCreateWindow(argv[0]);
3116   glutDisplayFunc(display);
3117
3118#ifndef XINETD
3119   //MainTransferFunctionWindow * tf_window;
3120   //tf_window = new MainTransferFunctionWindow();
3121   //tf_window->mainInit();
3122
3123   glutMouseFunc(mouse);
3124   glutMotionFunc(motion);
3125   glutKeyboardFunc(keyboard);
3126#endif
3127   glutIdleFunc(idle);
3128   glutReshapeFunc(resize_offscreen_buffer);
3129
3130   initGL();
3131   initTcl();
3132
3133#ifdef EVENTLOG
3134   init_event_log();
3135#endif
3136   //event loop
3137   glutMainLoop();
3138#ifdef EVENTLOG
3139   end_event_log();
3140#endif
3141
3142#ifdef XINETD
3143   end_service();
3144#endif
3145
3146   return 0;
3147}
Note: See TracBrowser for help on using the repository browser.