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

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

Fixed the nanoVIS server to recognize 1-6 numbers per line in the
OpenDX format.

Fixed the starting script to include the proper list of libraries
for LD_LIBRARY_PATH. Otherwise, crontab-launched servers don't
load properly and immediately die.

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