#include #include #include #include #include #include #ifdef UNIX #include #include #endif #ifdef WIN32 #define M_PI 3.14159265 #include #endif #include #include #include #include #include "vli.h" #include "vliutils.h" /** ** Copyright (c) 2001 San Diego Supercomputer Center (SDSC), ** University of California San Diego ** ** Users and possessors of this source code are hereby granted a ** nonexclusive, royalty-free copyright and design patent license to ** use this code in individual software. License is not granted for ** commercial resale, in whole or in part, without prior written ** permission from SDSC. This source is provided "AS IS" without express ** or implied warranty of any kind. ** **/ static char Copyright[] = { "Copyright (c) 2001 San Diego Supercomputer Center, University of California San Diego" }; /** ** TO DO: ** ** Button to stretch color scale to alpha range ** ** Handle .vols files ** ** Save and Read State file ** ** Reset LUT in Reset() ** ** Option to do parallel and crossed stereo in 2 viewports ** ** Use GLUI rotator ** (Interpolate using quaternions?) ** **/ /** ** constants: **/ /* the escape key: */ #define ESCAPE 0x1b /* title, lower-left corner, and size of the graphics window: */ const char *GRWINDOWTITLE = { "SDSC Volume Explorer, v2.00 Beta" }; const int GRWIN_LEFT = { 30 }; const int GRWIN_TOP = { 30 }; const int GRWIN_SIZE = { 600 }; /* title, lower-left corner, and size of the lut window: */ const char *LUTWINDOWTITLE = { "SDSC Volume Explorer LUT Sculpting" }; const int LUTWIN_LEFT = { 30 }; const int LUTWIN_TOP = { 650 }; const int LUTWIN_XSIZE = { 400 }; const int LUTWIN_YSIZE = { 300 }; /* these are all here to support the glui window, which is used */ /* for messages and user interface stuff: */ const char *UIWINDOWTITLE = { "SDSC Volume Explorer User Interface" }; const int UIWIN_LEFT = { 800 }; const int UIWIN_TOP = { 100 }; /* input file magic numbers: */ const char *VOXMAGICNUMBER = {"Vox1999a\n"}; const char *VOLSMAGICNUMBER = {"VOLS\n"}; /* file types: */ const int VOXFILE = { 0 }; const int VOLSFILE = { 1 }; /* minimum scale factor allowed: */ const float MIN_SCALE = { 0.1f }; /* multiplication factors for input interaction: */ /* (these are known from previous experience) */ const float ANGFACT = { 1.0 }; const float SCLFACT = { 0.005f }; /* active mouse buttons (or them together): */ const int LEFT = { 4 }; const int MIDDLE = { 2 }; const int RIGHT = { 1 }; /* which stereo view to use: */ #define STEREOLEFT 0 #define STEREOMIDDLE 1 #define STEREORIGHT 2 /* window background color (rgba): */ const float BGCOLOR[] = { 0.f,0.f,0.f,0.f }; /* color and line width for the axes: */ const float AXES_COLOR[] = { 0.,1.,1. }; const float AXES_WIDTH = { 2. }; /* what a wide sculpting line looks like: */ const float WIDE = { 2. }; /* big and small points: */ const float BIGPOINTS = { 6. }; const float SMALLPOINTS = { 3. }; /* picking tolerances: */ const float STOL = { 3.0 }; const float VTOL = { 0.05f }; /* change in angle for stereo views: */ const float STEREOANG = { 3. }; /* constant arguments for the button callback: */ #define QUIT 0 #define RESET 1 #define RECORD0 2 #define RECORD1 3 #define GOTO0 4 #define GOTO1 5 #define ANIM 6 #define GRAYSCALE 7 #define RAINBOW 8 #define HEATEDOBJ 9 #define BROWNGREEN 10 #define REDOCLT 11 #define REDOALT 12 #define CUTTINGPLANE 13 #define VOLUMEFILE 14 #define SLIDER (-2) #define CUSTOM (-1) /* animation modes: */ #define ANIMOFF 0 #define ANIMFORFOR 1 #define ANIMREVREV 2 #define ANIMFORREV 3 /* whether the left button is rotate or scale: */ const int ROTATE = { 0 }; const int SCALE = { 1 }; /* file types to save as: */ #define SAVEPPM 0 #define SAVEVOLC 1 #define SAVESTATE 2 /* channels to sculpt: */ #define RED 0 #define GREEN 1 #define BLUE 2 #define ALPHA 3 /* default volume file to read: */ const char *VOLFILE = { "smallhead.vox" }; /* status of trying to read vox file: */ #define OK 0 #define DOESNTEXIST 1 #define CANTREAD 2 #define CANTINIT 3 #define BADTYPE 4 /* which vp500 board buffers to use: */ const int VOLBUFFER = { 0 }; /* volume min and max values: */ #define VOLMIN 0 #define VOLMAX ( LutSize - 1 ) #define VOLXCROPMIN 0 #define VOLXCROPMAX VolNx #define VOLYCROPMIN 0 #define VOLYCROPMAX VolNy #define VOLZCROPMIN 0 #define VOLZCROPMAX VolNz #define BLENDREGIONMIN 1 #define BLENDREGIONMAX ( LutSize / 1 ) #define ORIGBLENDREGION 10 #define MAXALPHAMIN 0. #define MAXALPHAMAX 1. #define ORIGMAXALPHA 0.5 const int MINANIMFRAMES = { 10 }; const int MAXANIMFRAMES = { 200 }; /* which item to rotate with the left mouse button: */ const int ROTVOLUMEONLY = { 0 }; const int ROTVOLUMELIGHT = { 1 }; const int ROTLIGHTONLY = { 2 }; const int ROTCUTPLANE = { 3 }; /* id's for the slider callback: */ #define VOL 0 #define VOLXCROP 1 #define VOLYCROP 2 #define VOLZCROP 3 #define MAXALPHA 4 #define BLENDREGION 5 #define CUTPLANE 6 #define EMISSION 7 #define DIFFUSE 8 #define ROLL 9 #define ANIMFRAMES 10 #define ANIMT 11 #define NOWVOLUME 12 /* slider widths in pixels: */ const int SLIDERWIDTH = { 200 }; /* labels to put on the range sliders: */ const char *VOLFORMAT = { "Bandpass Opacity: %d - %d" }; const char *VOLXCROPFORMAT = { "X Cropping: %d - %d" }; const char *VOLYCROPFORMAT = { "Y Cropping: %d - %d" }; const char *VOLZCROPFORMAT = { "Z Cropping: %d - %d" }; const char *MAXALPHAFORMAT = { "Maximum Opacity = %.2f" }; const char *BLENDREGIONFORMAT = { "Blend Region Size = %d" }; const char *CUTPLANEFORMAT = { "Cutting Plane Location = %5.0f" }; const char *EMISSIONFORMAT = { "%3d%% Emission -- %3d%% Light Model" }; const char *DIFFUSEFORMAT = { "%3d%% Diffuse -- %3d%% Specular" }; const char *ROLLFORMAT = { "CLT Roll = %4d" }; const char *ANIMFRAMESFORMAT = { "%5d Animation Frames" }; const char *ANIMTFORMAT = { "Frame Number: %5d" }; const char *VOLNUMBERFORMAT = { "Volume Number: %5d" }; /* initial cutting plane parameters: */ const float CUTA = { 1. }; const float CUTB = { 1. }; const float CUTC = { 1. }; const float CUTD = { 0. }; /* initial light direction: */ const float LIGHTVX = { 0. }; const float LIGHTVY = { 0. }; const float LIGHTVZ = { 1. }; /* specular exponent in lighting model: */ const float SPECULAR_EXPONENT = { 2. }; /* projection options: */ const int ORTHO = { 0 }; const int PERSP = { 1 }; /* handy to have around: */ #ifndef FALSE const int FALSE = { 0 }; const int TRUE = { 1 }; #endif const int GLUIFALSE = { 0 }; const int GLUITRUE = { 1 }; const int OFF = { 0 }; const int ON = { 1 }; const int BANDPASS = { 0 }; const int BANDREJECT = { 1 }; inline float Sqr( float x ) { return x * x; } inline int Sqr( int x ) { return x * x; } inline float SIGN( float x ) { return ( x >= 0. ) ? 1. : -1.; } inline float SIGN( int x ) { return ( x >= 0 ) ? 1. : -1.; } inline float Lerp( float t, float a, float b ) { return (1.-t)*a + t*b; } inline int Lerp( float t, int a, int b ) { return (int)( Lerp( t, (float)a, (float)b ) + 0.5 ); } /** ** structure to hold a sculpted lut point: **/ struct scpt { float s; /* scalar value */ float v; /* intensity value */ struct scpt *prev, *next; /* doubly-linked list */ }; /** ** structure to hold a histogram bucket entry: **/ struct bucket { int n; /* # of occurances */ float logn; /* ln(n) */ }; /** ** structure to hold information on a volume: **/ struct volume { char VolumeFilename[256], *LutFilename; /* input files */ unsigned int VolNx, VolNy, VolNz; /* volume dimensions */ VLIVolume *VolData; /* the volume in VP500 land */ }; /** ** state structure: **/ struct state { int AxesOn; /* ON or OFF */ int Band; /* PASS or REJECT */ int BlendRegion, dum1; /* range to ramp up/down opacity value */ int BoundingBoxOn; /* TRUE means to draw a bounding box */ float CutA, CutB, CutC, CutD; /* cutting plane equation */ int CutPlaneOn; /* ON or OFF */ float DiffuseSpecular; /* 0.0 = diffuse only / 1.0 = specular only */ float EmissionLightModel; /* 0.0 = emission only / 1.0 = light model only */ float LutRed[4096], LutGreen[4096], LutBlue[4096]; /* color arrays */ float MaxAlpha; /* maximum opacity value */ int Negative; /* TRUE means to use the negative of the colors */ int NowVolume; /* which volume file are we looking at now */ int Projection; /* PERSP or ORTHO */ int Roll; /* how much to roll the CLT */ float Scale, Scale2; /* scaling factor */ int StereoView; /* STEREOLEFT, STEREORIGHT, STEREOMIDDLE*/ float StereoAng; /* add to Yang to get stereo view */ int VolMin, VolMax; /* from range slider */ int VolXCropMin, VolXCropMax; /* from range slider */ int VolYCropMin, VolYCropMax; /* from range slider */ int VolZCropMin, VolZCropMax; /* from range slider */ float Xang, Yang; /* volume rotation angles in degrees */ float XangCut, YangCut; /* cut plane rotation angles in degs */ float XangLight, YangLight; /* light dir rotation angles in degs */ }; /** ** global variables: **/ int ActiveButton; /* current button that is down */ int AnimMode; /* ANIMOFF, ... */ int AnimFrames; /* # frames in the animation */ float AnimDt; /* change in t per frame */ float AnimT; /* current t in the animation */ int AxesDL; /* axes display list */ struct bucket *Buckets; /* array of histogram buckets */ int ColorScale; /* current color scale to use */ int CurrentChannel; /* RED, GREEN, BLUE, ALPHA */ struct scpt * CurrentPoint; /* points to point being sculpted */ float CutPlaneMin, CutPlaneMax; /* limits on CutD */ float CutThick; /* cutting plane thickness */ GLUI * Glui; /* instance of glui window */ int GraphicsWindow; /* window id for top-level window */ int HistogramDL; /* histogram display list */ float HistoMax; /* maximum ln(n) histogram value */ int LutActiveButton; /* current button that is down */ int LutSize; /* 256 or 4096 */ int LutWindow; /* lut-sculpting window */ int LutXmouse, LutYmouse; /* mouse values */ int NumVolumes; /* # of volume files read */ int Projection; /* ORTHO or PERSP */ float Red, Green, Blue; /* star colors */ struct scpt *RedPts, *GreenPts, *BluePts, *AlphaPts; /* lists of sculpted points */ float RotMatrix[4][4]; /* set by glui rotation widget */ GLUI_String SaveFilename; /* filename to save-as */ int SaveFormat; /* SAVESTATE, SAVEVOLC, SAVEPPM */ GLUI * SaveWindow; /* window for the save-as dialog box */ struct state State; /* current state */ struct state State0, State1; /* 2 animation states */ char * StateFilename; /* name of state file */ int TransformMode; /* ROTATE or SCALE */ float TransXYZ[3]; /* set by glui translation widgets */ int UiWindow; /* the glut id for the ui window */ int Verbose; /* non-zero means print info */ unsigned int VolNx, VolNy, VolNz; /* volume dimensions */ struct volume *Volumes; /* holds the list of volume files read */ int WhichRotate; /* ROTCUTPLANE or ROTVOLUMELIGHT */ int Xmouse, Ymouse; /* mouse values */ GLUI_StaticText *VolLabel; GLUI_StaticText *VolXCropLabel; GLUI_StaticText *VolYCropLabel; GLUI_StaticText *VolZCropLabel; GLUI_StaticText *MaxAlphaLabel; GLUI_StaticText *BlendRegionLabel; GLUI_StaticText *CutPlaneLabel; GLUI_StaticText *EmmissionLabel; GLUI_StaticText *DiffuseLabel; GLUI_StaticText *RollLabel; GLUI_StaticText *AnimFramesLabel; GLUI_StaticText *AnimTLabel; GLUI_StaticText *VolNumberLabel; GLUI_Listbox *VolFileList; VLIContext * VolContext; VLICrop VolCrop; VLICutPlane * VolCutPlane; VLILight * VolLight; VLILookupTable * VolLUT; VLIMatrix VolMatrix; VLIVector3D Xaxis = VLIVector3D( 1., 0., 0. ); VLIVector3D Yaxis = VLIVector3D( 0., 1., 0. ); /** ** function prototypes: **/ void Animate( void ); void Buttons( int ); void CloseSave( int ); void CreateHistogram( void ); void Display( void ); void DisplayBoundingBox( void ); void DisplayBoundingFace( float, float, float, float, float, float, float, float, float, float, float, float ); void DisplayVolume( void ); void DoRasterString( float, float, float, char * ); void DoStrokeString( float, float, float, float, char * ); void FreeAllColorLists( void ); void FreeList( void ); void GetStateFilename( void ); void Help( void ); void InitAnimation( int ); void InitGraphics( void ); void InitHistogram( void ); void InitLists( void ); void InitUi( void ); int InitVolumeBoard( void ); int InitVolumeInfo( void ); void InsertColor( float, float, float, float ); struct scpt * InsertPoint( float, float ); void InterpolateState( float ); void Keyboard( unsigned char, int, int ); void LutDisplay( void ); void LutMouseButton( int, int, int, int ); void LutMouseMotion( int, int ); void LutResize( int, int ); void LutVisibility( int ); void MouseButton( int, int, int, int ); void MouseMotion( int, int ); struct scpt * PickPoint( float, float ); void Quit( void ); float Ranf( float, float ); void ReadState( void ); int ReadVolume( struct volume * ); void Reset( void ); void Resize( int, int ); void SaveFile( int ); void SaveFileDialog( int ); double Seconds( void ); void SetAlphaRange( int ); void SetCLT( void ); void SetColorRange( int ); void SetLightModel( void ); void Sliders( int ); void SwapBytes( unsigned char * ); void Visibility( int ); void VolAxes( void ); int WritePpm( char *); int WriteState( char * ); int WriteVolc( char * ); unsigned sleep( unsigned ); /** ** main program: **/ int main( int argc, char *argv[] ) { int i; /* counter */ int stat; /* status of reading the vox file */ int v; /* volume number we arereading */ FILE *fp; /* try reading a file to see if it exists */ /* turn on the glut package: */ /* (do this before checking argc and argv since it might */ /* pull some command line arguments out) */ #ifndef STUBS glutInit( &argc, argv ); #endif /* set defaults: */ Verbose = FALSE; NumVolumes = 0; /* read the command line: */ for( i=1; i < argc; i++ ) { if( argv[i][0] == '-' ) { switch( argv[i][1] ) { case 'D': case 'v': case 'V': Verbose = TRUE; break; default: fprintf( stderr, "Unknown argument: '%s'\n", argv[i] ); fprintf( stderr, "Usage: %s [-v] [voxfilename]\n", argv[0] ); } } else { NumVolumes++; } } /* light up the volume board: */ stat = InitVolumeBoard(); if( stat == CANTINIT ) { fprintf( stderr, "Cannot initialize the VP 500 board\n" ); #ifndef WIN32 sleep( 2 ); #endif exit( 1 ); } /* create a list of the volume files found and see if they exist: */ if( NumVolumes > 0 ) { Volumes = new struct volume[NumVolumes]; v = 0; for( i=1; i < argc; i++ ) { if( argv[i][0] == '-' ) { /* no command line arguments so far use more than */ /* one argv, so don't have to test for them: */ } else { /* see if the file even exists: */ fp = fopen( argv[i], "r" ); if( fp == NULL ) { fprintf( stderr, "Volume file '%s' does not exist\n", argv[i] ); NumVolumes--; continue; } fclose( fp ); strcpy( Volumes[v].VolumeFilename, argv[i] ); stat = ReadVolume( &Volumes[v] ); if( stat != OK ) { fprintf( stderr, "Cannot read volume file '%s'\n", Volumes[v].VolumeFilename ); NumVolumes--; } else { v++; } } } } /* read the volume filename from standard input: */ if( NumVolumes == 0 ) Volumes = new struct volume[1]; while( NumVolumes == 0 ) { /* prompt for the file: */ printf( "Enter .vox file: " ); fflush( stdout ); scanf( "%s", Volumes[0].VolumeFilename ); /* try opening: */ fp = fopen( Volumes[0].VolumeFilename, "r" ); if( fp == NULL ) { fprintf( stderr, "Volume file '%s' does not exist\n", Volumes[0].VolumeFilename ); continue; } fclose( fp ); /* try reading: */ stat = ReadVolume( &Volumes[0] ); switch( stat ) { case OK: NumVolumes++; break; case CANTREAD: continue; } } State.NowVolume = 0; /* init all the global variables used by Display(): */ /* this will also post a redisplay */ /* it is important to call this before InitUi(): */ /* so that the variables that glui will control are correct */ /* when each glui widget is created */ Reset(); /* setup all the graphics stuff, including callbacks: */ InitGraphics(); /* Initialize the histogram data structure and display list: */ InitHistogram(); /* setup the volume info based on the file(s) that were read: */ InitVolumeInfo(); /* setup all the user interface stuff: */ InitUi(); /* be sure 2 animation states start out with same info: */ State0 = State; State1 = State; /* prime for animation: */ State0.NowVolume = 0; State1.NowVolume = NumVolumes - 1; /* draw the scene once and wait for some interaction: */ /* (will never return) */ glutMainLoop(); /* keep lint happy: */ return 0; } /** ** this is where one would put code that is to be called ** everytime the glut main loop has nothing to do, ie, ** the glut idle function ** ** this is typically where animation happens **/ void Animate( void ) { float dt; /* possibly changed delta-t */ char str[256]; /* sprintf string */ dt = 1. / (float)AnimFrames; AnimDt = dt * SIGN( AnimDt ); if( AnimMode == GOTO0 ) AnimDt = -dt; if( AnimMode == GOTO1 ) AnimDt = dt; switch( AnimMode ) { case ANIMOFF: /* shouldn't be here if animation is off: */ GLUI_Master.set_glutIdleFunc( NULL ); break; case ANIMFORFOR: AnimDt = dt; break; case ANIMREVREV: AnimDt = -dt; break; case ANIMFORREV: AnimDt = dt * SIGN( AnimDt ); break; case GOTO0: AnimDt = -dt; break; case GOTO1: AnimDt = dt; break; } AnimT += AnimDt; switch( AnimMode ) { case ANIMFORFOR: if( AnimT > 1. ) AnimT -= 1.; break; case ANIMREVREV: if( AnimT < 0. ) AnimT += 1.; break; case ANIMFORREV: if( AnimT > 1. ) { AnimT = 1.; AnimDt = -dt; } if( AnimT < 0. ) { AnimT = 0.; AnimDt = dt; } break; case GOTO0: if( AnimT <= 0. ) { AnimT = 0.; GLUI_Master.set_glutIdleFunc( NULL ); AnimMode = ANIMOFF; } break; case GOTO1: if( AnimT >= 1. ) { AnimT = 1.; GLUI_Master.set_glutIdleFunc( NULL ); AnimMode = ANIMOFF; } break; } InterpolateState( AnimT ); sprintf( str, ANIMTFORMAT, (int)( AnimFrames*AnimT) ); AnimTLabel->set_text( str ); Glui->sync_live(); glutSetWindow( GraphicsWindow ); glutPostRedisplay(); } /** ** glui buttons callback: **/ void Buttons( int id ) { float dt; /* possibly changed delta-t */ char str[256]; /* label string */ VLIStatus status; switch( id ) { case ANIM: dt = 1. / (float)AnimFrames; switch( AnimMode ) { case ANIMOFF: GLUI_Master.set_glutIdleFunc( NULL ); break; case ANIMFORFOR: GLUI_Master.set_glutIdleFunc( Animate ); AnimDt = dt; break; case ANIMREVREV: GLUI_Master.set_glutIdleFunc( Animate ); AnimDt = -dt; break; case ANIMFORREV: GLUI_Master.set_glutIdleFunc( Animate ); AnimDt = dt; break; } break; case GRAYSCALE: ColorScale = GRAYSCALE; FreeAllColorLists(); SetColorRange( GRAYSCALE ); break; case RAINBOW: ColorScale = RAINBOW; FreeAllColorLists(); SetColorRange( RAINBOW ); break; case HEATEDOBJ: ColorScale = HEATEDOBJ; FreeAllColorLists(); SetColorRange( HEATEDOBJ ); break; case BROWNGREEN: ColorScale = BROWNGREEN; FreeAllColorLists(); SetColorRange( BROWNGREEN ); break; case GOTO0: AnimMode = GOTO0; GLUI_Master.set_glutIdleFunc( Animate ); break; case GOTO1: AnimMode = GOTO1; GLUI_Master.set_glutIdleFunc( Animate ); break; case QUIT: /* gracefully close the glui window: */ /* gracefully close out the graphics: */ /* gracefully close the graphics window: */ /* gracefully exit the program: */ Quit(); break; case RECORD0: State0 = State; AnimT = 0.; Sliders( ANIMT ); break; case RECORD1: State1 = State; AnimT = 1.; Sliders( ANIMT ); break; case RESET: Reset(); sprintf( str, VOLFORMAT, State.VolMin, State.VolMax ); VolLabel->set_text( str ); sprintf( str, MAXALPHAFORMAT, State.MaxAlpha ); MaxAlphaLabel->set_text( str ); sprintf( str, BLENDREGIONFORMAT, State.BlendRegion ); BlendRegionLabel->set_text( str ); sprintf( str, VOLXCROPFORMAT, State.VolXCropMin, State.VolXCropMax ); VolXCropLabel->set_text( str ); sprintf( str, VOLYCROPFORMAT, State.VolYCropMin, State.VolYCropMax ); VolYCropLabel->set_text( str ); sprintf( str, VOLZCROPFORMAT, State.VolZCropMin, State.VolZCropMax ); VolZCropLabel->set_text( str ); sprintf( str, CUTPLANEFORMAT, -State.CutD ); CutPlaneLabel->set_text( str ); sprintf( str, ROLLFORMAT, State.Roll ); RollLabel->set_text( str ); sprintf( str, ANIMFRAMESFORMAT, AnimFrames ); AnimFramesLabel->set_text( str ); sprintf( str, EMISSIONFORMAT, (int)(100.*1.), (int)(100.*1.) ); EmmissionLabel->set_text( str ); sprintf( str, DIFFUSEFORMAT, (int)(100.*1.), (int)(100.*1.) ); DiffuseLabel->set_text( str ); sprintf( str, ANIMTFORMAT, (int)( AnimFrames*AnimT) ); AnimTLabel->set_text( str ); if( NumVolumes > 1 ) { sprintf( str, VOLNUMBERFORMAT, State.NowVolume ); VolNumberLabel->set_text( str ); } InitVolumeInfo(); State0 = State; State1 = State; State0.NowVolume = 0; State1.NowVolume = NumVolumes - 1; break; case REDOCLT: SetCLT(); CreateHistogram(); glutSetWindow( LutWindow ); glutPostRedisplay(); break; case REDOALT: SetAlphaRange( SLIDER ); glutSetWindow( LutWindow ); glutPostRedisplay(); break; case CUTTINGPLANE: status = VolContext->RemoveCutPlane( VolCutPlane ); if( State.CutPlaneOn ) { status = VolContext->AddCutPlane( VolCutPlane ); if( status != kVLIOK ) { fprintf( stderr, "AddCutPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } break; case VOLUMEFILE: VolFileList->set_int_val( State.NowVolume ); sprintf( str, VOLNUMBERFORMAT, State.NowVolume ); VolNumberLabel->set_text( str ); break; } Glui->sync_live(); } /** ** close the save-as window: **/ void CloseSave( int id ) { SaveWindow->close(); } /** ** draw the histogram into a display list: ** ** do this everytime the color ranges change: **/ void CreateHistogram( void ) { int i; /* counter */ int ii; /* counter after clt rolling */ struct bucket *b; /* pointer into Buckets array */ if( Verbose ) fprintf( stderr, "CreateHistogram()\n" ); glutSetWindow( LutWindow ); glNewList( HistogramDL, GL_COMPILE ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluOrtho2D( 0., (float)LutSize, 0., HistoMax ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); #define DIMFACTOR 0.90 glBegin( GL_QUAD_STRIP ); for( i = 0, b = Buckets; i < LutSize; i++, b++ ) { ii = i + State.Roll; if( ii >= LutSize ) ii -= LutSize; if( ! State.Negative ) glColor3f( DIMFACTOR*State.LutRed[ii], DIMFACTOR*State.LutGreen[ii], DIMFACTOR*State.LutBlue[ii] ); else glColor3f( DIMFACTOR*(1.-State.LutRed[ii]), DIMFACTOR*(1.-State.LutGreen[ii]), DIMFACTOR*(1.-State.LutBlue[ii]) ); glVertex2f( (float)i, 0. ); glVertex2f( (float)i, b->logn ); } glEnd(); glColor3f( 1., 1., 1. ); glBegin( GL_LINE_STRIP ); for( i = 0, b = Buckets; i < LutSize; i++, b++ ) { glVertex2f( (float)i, b->logn ); } glEnd(); glEndList(); glutPostRedisplay(); } /** ** draw the complete scene: **/ void Display( void ) { int dx, dy, d; /* viewport dimensions */ int xl, yb; /* lower-left corner of viewport */ float w; /* width of viewing volume */ /* set which window we want to do the graphics into: */ glutSetWindow( GraphicsWindow ); /* erase the background: */ glDrawBuffer( GL_BACK ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glEnable( GL_DEPTH_TEST ); /* specify shading to be flat: */ glShadeModel( GL_FLAT ); /* set the viewport to a square centered in the window: */ dx = glutGet( GLUT_WINDOW_WIDTH ); dy = glutGet( GLUT_WINDOW_HEIGHT ); d = dx < dy ? dx : dy; /* minimum dimension */ xl = ( dx - d ) / 2; yb = ( dy - d ) / 2; glViewport( xl, yb, d, d ); /* setup the viewing volume: */ glMatrixMode( GL_PROJECTION ); glLoadIdentity(); w = sqrt( VolNx*VolNx + VolNy*VolNy + VolNz*VolNz ); if( Projection == ORTHO ) glOrtho( -w/2., w/2., -w/2., w/2., -10.*w, 10.*w ); else { gluPerspective( 90., 1., 0.01, 10.*w ); glTranslatef( 0., 0., -w/2. ); } glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glScalef( State.Scale, State.Scale, State.Scale ); /* display the volume: */ DisplayVolume(); /* here comes the graphics that needs a transformation: */ switch( State.StereoView ) { case STEREOLEFT: State.StereoAng = STEREOANG; break; case STEREOMIDDLE: State.StereoAng = 0.; break; case STEREORIGHT: State.StereoAng = -STEREOANG; break; } glPushMatrix(); glRotatef( State.Yang + State.StereoAng, 0., 1., 0. ); glRotatef( State.Xang, 1., 0., 0. ); glTranslatef( -(float)VolNx/2., -(float)VolNy/2., -(float)VolNz/2. ); if( State.BoundingBoxOn ) { DisplayBoundingBox(); } if( State.AxesOn == ON ) { glCallList( AxesDL ); } glPopMatrix(); /* swap the double-buffered framebuffers: */ glutSwapBuffers(); /* be sure the graphics buffer has been sent: */ glFlush(); } /** ** display the bounding box: **/ void DisplayBoundingBox( void ) { VLIVector3D vector; /* light direction */ float f; /* scale factor for light vector */ float x0, vx, y0, vy, z0, vz; /* light vector */ float tstar; /* solution to q0 + t*vq = 0. */ float tmin; /* first time vector emerges from the cropped volume */ float d; /* denominator */ if( State.CutPlaneOn == OFF ) { glColor3f( 1., 1., 1. ); glLineWidth( 2. ); glBegin( GL_LINE_STRIP ); glVertex3f( State.VolXCropMin, State.VolYCropMin, State.VolZCropMin ); glVertex3f( State.VolXCropMax, State.VolYCropMin, State.VolZCropMin ); glVertex3f( State.VolXCropMax, State.VolYCropMax, State.VolZCropMin ); glVertex3f( State.VolXCropMin, State.VolYCropMax, State.VolZCropMin ); glVertex3f( State.VolXCropMin, State.VolYCropMin, State.VolZCropMin ); glVertex3f( State.VolXCropMin, State.VolYCropMin, State.VolZCropMax ); glVertex3f( State.VolXCropMax, State.VolYCropMin, State.VolZCropMax ); glVertex3f( State.VolXCropMax, State.VolYCropMax, State.VolZCropMax ); glVertex3f( State.VolXCropMin, State.VolYCropMax, State.VolZCropMax ); glVertex3f( State.VolXCropMin, State.VolYCropMin, State.VolZCropMax ); glEnd(); glBegin( GL_LINES ); glVertex3f( State.VolXCropMax, State.VolYCropMin, State.VolZCropMin ); glVertex3f( State.VolXCropMax, State.VolYCropMin, State.VolZCropMax ); glVertex3f( State.VolXCropMax, State.VolYCropMax, State.VolZCropMin ); glVertex3f( State.VolXCropMax, State.VolYCropMax, State.VolZCropMax ); glVertex3f( State.VolXCropMin, State.VolYCropMax, State.VolZCropMin ); glVertex3f( State.VolXCropMin, State.VolYCropMax, State.VolZCropMax ); glEnd(); glLineWidth( 1. ); } else { /* do the bounding box for the cut volume: */ glColor3f( 1., 1., 1. ); glLineWidth( 2. ); DisplayBoundingFace( State.VolXCropMin, State.VolYCropMin, State.VolZCropMax, State.VolXCropMax, State.VolYCropMin, State.VolZCropMax, State.VolXCropMax, State.VolYCropMax, State.VolZCropMax, State.VolXCropMin, State.VolYCropMax, State.VolZCropMax ); DisplayBoundingFace( State.VolXCropMin, State.VolYCropMin, State.VolZCropMin, State.VolXCropMin, State.VolYCropMax, State.VolZCropMin, State.VolXCropMax, State.VolYCropMax, State.VolZCropMin, State.VolXCropMax, State.VolYCropMin, State.VolZCropMin ); DisplayBoundingFace( State.VolXCropMax, State.VolYCropMin, State.VolZCropMax, State.VolXCropMax, State.VolYCropMin, State.VolZCropMin, State.VolXCropMax, State.VolYCropMax, State.VolZCropMin, State.VolXCropMax, State.VolYCropMax, State.VolZCropMax ); DisplayBoundingFace( State.VolXCropMin, State.VolYCropMin, State.VolZCropMax, State.VolXCropMin, State.VolYCropMax, State.VolZCropMax, State.VolXCropMin, State.VolYCropMax, State.VolZCropMin, State.VolXCropMin, State.VolYCropMin, State.VolZCropMin ); DisplayBoundingFace( State.VolXCropMin, State.VolYCropMax, State.VolZCropMax, State.VolXCropMax, State.VolYCropMax, State.VolZCropMax, State.VolXCropMax, State.VolYCropMax, State.VolZCropMin, State.VolXCropMin, State.VolYCropMax, State.VolZCropMin ); DisplayBoundingFace( State.VolXCropMin, State.VolYCropMin, State.VolZCropMax, State.VolXCropMin, State.VolYCropMin, State.VolZCropMin, State.VolXCropMax, State.VolYCropMin, State.VolZCropMin, State.VolXCropMax, State.VolYCropMin, State.VolZCropMax ); glLineWidth( 1. ); } /* possibly do the light vector: */ if( State.EmissionLightModel > 0.0 ) { /* light vector: */ x0 = (float)VolNx/2.; y0 = (float)VolNy/2.; z0 = (float)VolNz/2.; f = 0.67 * sqrt( VolNx*VolNx + VolNy*VolNy + VolNz*VolNz ); vector = VolLight->GetDirection(); vx = -f * vector.X(); vy = -f * vector.Y(); vz = -f * vector.Z(); /* start with tmin = 1., and look for the min positive t*: */ tmin = 1.; if( vx != 0. ) { tstar = ( 0. - x0 ) / vx; if( tstar > 0. && tstar < tmin ) tmin = tstar; tstar = ( VolNx - x0 ) / vx; if( tstar > 0. && tstar < tmin ) tmin = tstar; } if( vy != 0. ) { tstar = ( 0. - y0 ) / vy; if( tstar > 0. && tstar < tmin ) tmin = tstar; tstar = ( VolNy - y0 ) / vy; if( tstar > 0. && tstar < tmin ) tmin = tstar; } if( vz != 0. ) { tstar = ( 0. - z0 ) / vz; if( tstar > 0. && tstar < tmin ) tmin = tstar; tstar = ( VolNz - z0 ) / vz; if( tstar > 0. && tstar < tmin ) tmin = tstar; } if( State.CutPlaneOn == ON ) { d = State.CutA*vx + State.CutB*vy + State.CutC*vz; if( d != 0. ) { tstar = - ( State.CutA*x0 + State.CutB*y0 + State.CutC*z0 + State.CutD ) / d; if( tstar > 0. && tstar < tmin ) tmin = tstar; } } glColor3f( 1., 1., 0. ); glLineWidth( 2. ); glBegin( GL_LINE_STRIP ); glVertex3f( x0 + tmin*vx, y0 + tmin*vy, z0 + tmin*vz ); glVertex3f( x0 + vx, y0 + vy, z0 + vz ); glEnd(); glLineWidth( 1. ); } } /** ** draw one face of the cut bounding box: **/ void DisplayBoundingFace( float x0, float y0, float z0, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3 ) { int mask; /* 4 bit number signifying out-of-cut-bounds */ /* status of points 3-2-1-0 */ float t01, t12, t23, t30; /* parametric interpolators */ float s0, s1, s2, s3; /* cutting plane signed distances */ /** ** 3-----2 ** | | ** | | ** | | ** 0-----1 **/ s0 = State.CutA*x0 + State.CutB*y0 + State.CutC*z0 + State.CutD; s1 = State.CutA*x1 + State.CutB*y1 + State.CutC*z1 + State.CutD; s2 = State.CutA*x2 + State.CutB*y2 + State.CutC*z2 + State.CutD; s3 = State.CutA*x3 + State.CutB*y3 + State.CutC*z3 + State.CutD; mask = 0; if( s0 < 0. ) mask |= 1; if( s1 < 0. ) mask |= 2; if( s2 < 0. ) mask |= 4; if( s3 < 0. ) mask |= 8; glBegin( GL_LINE_LOOP ); switch( mask ) { case 0: break; case 1: t01 = -s0 / ( s1 - s0 ); t30 = -s3 / ( s0 - s3 ); glVertex3f( Lerp(t30,x3,x0), Lerp(t30,y3,y0), Lerp(t30,z3,z0) ); glVertex3f( x0, y0, z0 ); glVertex3f( Lerp(t01,x0,x1), Lerp(t01,y0,y1), Lerp(t01,z0,z1) ); break; case 2: t01 = -s0 / ( s1 - s0 ); t12 = -s1 / ( s2 - s1 ); glVertex3f( Lerp(t01,x0,x1), Lerp(t01,y0,y1), Lerp(t01,z0,z1) ); glVertex3f( x1, y1, z1 ); glVertex3f( Lerp(t12,x1,x2), Lerp(t12,y1,y2), Lerp(t12,z1,z2) ); break; case 3: t12 = -s1 / ( s2 - s1 ); t30 = -s3 / ( s0 - s3 ); glVertex3f( Lerp(t30,x3,x0), Lerp(t30,y3,y0), Lerp(t30,z3,z0) ); glVertex3f( x0, y0, z0 ); glVertex3f( x1, y1, z1 ); glVertex3f( Lerp(t12,x1,x2), Lerp(t12,y1,y2), Lerp(t12,z1,z2) ); break; case 4: t12 = -s1 / ( s2 - s1 ); t23 = -s2 / ( s3 - s2 ); glVertex3f( Lerp(t12,x1,x2), Lerp(t12,y1,y2), Lerp(t12,z1,z2) ); glVertex3f( x2, y2, z2 ); glVertex3f( Lerp(t23,x2,x3), Lerp(t23,y2,y3), Lerp(t23,z2,z3) ); break; case 6: t01 = -s0 / ( s1 - s0 ); t23 = -s2 / ( s3 - s2 ); glVertex3f( Lerp(t01,x0,x1), Lerp(t01,y0,y1), Lerp(t01,z0,z1) ); glVertex3f( x1, y1, z1 ); glVertex3f( x2, y2, z2 ); glVertex3f( Lerp(t23,x2,x3), Lerp(t23,y2,y3), Lerp(t23,z2,z3) ); break; case 7: t23 = -s2 / ( s3 - s2 ); t30 = -s3 / ( s0 - s3 ); glVertex3f( Lerp(t30,x3,x0), Lerp(t30,y3,y0), Lerp(t30,z3,z0) ); glVertex3f( x0, y0, z0 ); glVertex3f( x1, y1, z1 ); glVertex3f( x2, y2, z2 ); glVertex3f( Lerp(t23,x2,x3), Lerp(t23,y2,y3), Lerp(t23,z2,z3) ); break; case 8: t23 = -s2 / ( s3 - s2 ); t30 = -s3 / ( s0 - s3 ); glVertex3f( Lerp(t23,x2,x3), Lerp(t23,y2,y3), Lerp(t23,z2,z3) ); glVertex3f( x3, y3, z3 ); glVertex3f( Lerp(t30,x3,x0), Lerp(t30,y3,y0), Lerp(t30,z3,z0) ); break; case 9: t01 = -s0 / ( s1 - s0 ); t23 = -s2 / ( s3 - s2 ); glVertex3f( Lerp(t23,x2,x3), Lerp(t23,y2,y3), Lerp(t23,z2,z3) ); glVertex3f( x3, y3, z3 ); glVertex3f( x0, y0, z0 ); glVertex3f( Lerp(t01,x0,x1), Lerp(t01,y0,y1), Lerp(t01,z0,z1) ); break; case 11: t12 = -s1 / ( s2 - s1 ); t23 = -s2 / ( s3 - s2 ); glVertex3f( Lerp(t23,x2,x3), Lerp(t23,y2,y3), Lerp(t23,z2,z3) ); glVertex3f( x3, y3, z3 ); glVertex3f( x0, y0, z0 ); glVertex3f( x1, y1, z1 ); glVertex3f( Lerp(t12,x1,x2), Lerp(t12,y1,y2), Lerp(t12,z1,z2) ); break; case 12: t12 = -s1 / ( s2 - s1 ); t30 = -s3 / ( s0 - s3 ); glVertex3f( Lerp(t12,x1,x2), Lerp(t12,y1,y2), Lerp(t12,z1,z2) ); glVertex3f( x2, y2, z2 ); glVertex3f( x3, y3, z3 ); glVertex3f( Lerp(t30,x3,x0), Lerp(t30,y3,y0), Lerp(t30,z3,z0) ); break; case 13: t01 = -s0 / ( s1 - s0 ); t12 = -s1 / ( s2 - s1 ); glVertex3f( Lerp(t12,x1,x2), Lerp(t12,y1,y2), Lerp(t12,z1,z2) ); glVertex3f( x2, y2, z2 ); glVertex3f( x3, y3, z3 ); glVertex3f( x0, y0, z0 ); glVertex3f( Lerp(t01,x0,x1), Lerp(t01,y0,y1), Lerp(t01,z0,z1) ); break; case 14: t01 = -s0 / ( s1 - s0 ); t30 = -s3 / ( s0 - s3 ); glVertex3f( Lerp(t01,x0,x1), Lerp(t01,y0,y1), Lerp(t01,z0,z1) ); glVertex3f( x1, y1, z1 ); glVertex3f( x2, y2, z2 ); glVertex3f( x3, y3, z3 ); glVertex3f( Lerp(t30,x3,x0), Lerp(t30,y3,y0), Lerp(t30,z3,z0) ); break; case 15: glVertex3f( x0, y0, z0 ); glVertex3f( x1, y1, z1 ); glVertex3f( x2, y2, z2 ); glVertex3f( x3, y3, z3 ); break; default: fprintf( stderr, "ERROR: mask = %d\n", mask ); } glEnd(); } /** ** perform the specific display functions for the volume: **/ void DisplayVolume( void ) { VLIStatus status; int baseWidth, baseHeight; /* size of the texture map */ int imageWidth, imageHeight; /* what are these????? */ VLIVector2D hexTex[6]; /* the display texture coords */ VLIVector3D hexGeom[6]; /* the display coords */ VLIPixel *basePlane; /* will point to texels */ int i; /* counter */ /* render the volume into the base plane buffer on the board: */ status = VolContext->RenderBasePlane( Volumes[ State.NowVolume ].VolData, VOLBUFFER ); if( status != kVLIOK ) { fprintf( stderr, "RenderBasePlane(): %s\n", VLIUtGetErrorString( status ) ); return; } /* fetch the base plane from the board: */ status = VolContext->FetchBasePlane( VOLBUFFER, baseWidth, baseHeight, imageWidth, imageHeight, basePlane, hexGeom, hexTex ); if( status != kVLIOK ) { fprintf( stderr, "FetchBasePlane(): %s\n", VLIUtGetErrorString( status ) ); return; } if( Verbose ) { fprintf(stderr, "FetchBasePlane: base = ( %d x %d ) ; image = ( %d x %d )\n", baseWidth, baseHeight, imageWidth, imageHeight ); } /* setup the texture: */ glTexImage2D( GL_TEXTURE_2D, 0, 4, baseWidth, baseHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, basePlane ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); glEnable( GL_TEXTURE_2D ); /* display the volume: */ if( Verbose ) { fprintf( stderr, "\n\n Coords Texture\n" ); for( i = 0; i < 6; i++ ) { fprintf( stderr, "%7.2f %7.2f %7.2f %7.2f %7.2f\n", (float)hexGeom[i].X(), (float)hexGeom[i].Y(), (float)hexGeom[i].Z(), (float)hexTex[i].X(), (float)hexTex[i].Y() ); } } glColor3f( 1., 1., 1. ); glEnable( GL_TEXTURE_2D ); glPushMatrix(); glTranslatef( -(float)VolNx/2., -(float)VolNy/2., -(float)VolNz/2. ); if( Projection == ORTHO ) { glBegin( GL_POLYGON ); for( i = 0; i < 6; i++ ) { glTexCoord2f( (float)hexTex[i].X(), (float)hexTex[i].Y() ); glVertex3f( (float)hexGeom[i].X(), (float)hexGeom[i].Y(), (float)hexGeom[i].Z() ); } glEnd(); } else { glBegin( GL_TRIANGLE_FAN ); glTexCoord2f( (float)hexTex[0].X(), (float)hexTex[0].Y() ); glVertex3f( (float)hexGeom[0].X(), (float)hexGeom[0].Y(), (float)hexGeom[0].Z() ); glTexCoord2f( (float)hexTex[1].X(), (float)hexTex[1].Y() ); glVertex3f( (float)hexGeom[1].X(), (float)hexGeom[1].Y(), (float)hexGeom[1].Z() ); glTexCoord2f( (float)hexTex[2].X(), (float)hexTex[2].Y() ); glVertex3f( (float)hexGeom[2].X(), (float)hexGeom[2].Y(), (float)hexGeom[2].Z() ); glTexCoord2f( (float)hexTex[3].X(), (float)hexTex[3].Y() ); glVertex3f( (float)hexGeom[3].X(), (float)hexGeom[3].Y(), (float)hexGeom[3].Z() ); glTexCoord2f( (float)hexTex[4].X(), (float)hexTex[4].Y() ); glVertex3f( (float)hexGeom[4].X(), (float)hexGeom[4].Y(), (float)hexGeom[4].Z() ); glTexCoord2f( (float)hexTex[5].X(), (float)hexTex[5].Y() ); glVertex3f( (float)hexGeom[5].X(), (float)hexGeom[5].Y(), (float)hexGeom[5].Z() ); glEnd(); } glPopMatrix(); glDisable( GL_TEXTURE_2D ); VolContext->ReleaseBasePlane( VOLBUFFER ); } /** ** use glut to display a string of characters using a raster font: **/ void DoRasterString( float x, float y, float z, char *s ) { char c; /* one character to print */ glRasterPos3f( x, y, z ); for( ; ( c = *s ) != '\0'; s++ ) { glutBitmapCharacter( GLUT_BITMAP_TIMES_ROMAN_24, c ); } } /** ** use glut to display a string of characters using a stroke font: **/ void DoStrokeString( float x, float y, float z, float ht, char *s ) { char c; /* one character to print */ float sf; /* the scale factor */ glPushMatrix(); glTranslatef( x, y, z ); sf = ht / ( 119.05 + 33.33 ); glScalef( sf, sf, sf ); for( ; ( c = *s ) != '\0'; s++ ) { glutStrokeCharacter( GLUT_STROKE_ROMAN, c ); } glPopMatrix(); } /** ** free up all color channels' linked lists: **/ void FreeAllColorLists( void ) { int tempc; /* save current channel */ tempc = CurrentChannel; CurrentChannel = RED; FreeList(); CurrentChannel = GREEN; FreeList(); CurrentChannel = BLUE; FreeList(); CurrentChannel = tempc; } /** ** free up the linked list for the current channel's sculpted points: **/ void FreeList( void ) { struct scpt *p; /* current point */ struct scpt *pnext; /* next point */ /* find start of list: */ switch( CurrentChannel ) { case RED: p = RedPts; RedPts = NULL; break; case GREEN: p = GreenPts; GreenPts = NULL; break; case BLUE: p = BluePts; BluePts = NULL; break; case ALPHA: p = AlphaPts; AlphaPts = NULL; break; default: p = NULL; break; } /* if p is null, there is no list to walk: */ if( p == NULL ) return; /* walk the list: */ for( ; p != NULL; p = pnext ) { pnext = p->next; free( (char *)p ); } } /** ** get the name of the State file and see if we should read it: **/ void GetStateFilename( void ) { char *basename; /* vox filename without the suffix */ int n; /* # chars in basename */ int i; /* counter */ char c; /* character to test */ int wantToRead; /* TRUE means want to read lut file */ FILE *fp; /* see if can open LUT file */ /* get name of potential lut file: */ basename = strdup( Volumes[ State.NowVolume ].VolumeFilename ); n = strlen( basename ); for( i = n-1 ; i >= 0; i-- ) { c = basename[i]; if( c == '.' ) break; } /* create the lut filename and see if want to read it: */ wantToRead = FALSE; if( c == '.' ) { basename[i] = '\0'; StateFilename = new char[ strlen(basename) + 4 + 1]; sprintf( StateFilename, "%s.vx", basename ); fp = fopen( StateFilename, "r" ); if( fp != NULL ) { fclose( fp ); printf( "LUT file '%s' found -- read it? [y/n] ", StateFilename ); fflush( stdout ); /* flush previous \n's: */ while( ( c = getchar() ) == '\n' ) ; /* flush all chars up to , looking for a */ /* yes answer -- all other answers are no: */ while( c != '\n' ) { if( c == 'y' || c == 'Y' ) wantToRead = TRUE; c = getchar(); } } else { fprintf( stderr, "Cannot read LUT file '%s'\n", StateFilename ); } } else { fprintf( stderr, "Cannot create a LUT filename from VOX filename '%s'\n", Volumes[ State.NowVolume ].VolumeFilename ); } /* read the file if necessary: */ if( wantToRead ) { ReadState(); } /* done: */ free( basename ); } /** ** print out what keyboard keys are active: **/ void Help( void ) { fprintf( stderr, "b Toggle bounding box\n" ); fprintf( stderr, "h Print this list\n" ); fprintf( stderr, "q Quit\n" ); fprintf( stderr, "r Set left-button mode to 'rotate'\n" ); fprintf( stderr, "s Set left-button mode to 'scale'\n" ); fprintf( stderr, "C Generate middle-eye stereo (default)\n" ); fprintf( stderr, "L Generate left-eye stereo\n" ); fprintf( stderr, "M Generate middle-eye stereo (default)\n" ); fprintf( stderr, "ESC Quit\n" ); } /** ** initialize the glut and OpenGL libraries: ** also setup display lists and callback functions **/ void InitGraphics( void ) { if( Verbose ) fprintf( stderr, "InitGraphics\n" ); /* setup the display mode: */ /* ( *must* be done before call to glutCreateWindow() ) */ glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH ); /* open the graphics window and set its title: */ glutInitWindowSize( GRWIN_SIZE, GRWIN_SIZE ); glutInitWindowPosition( GRWIN_LEFT, GRWIN_TOP ); GraphicsWindow = glutCreateWindow( GRWINDOWTITLE ); glutSetWindowTitle( GRWINDOWTITLE ); /* setup the clear values: */ glClearColor( BGCOLOR[0], BGCOLOR[1], BGCOLOR[2], BGCOLOR[3] ); /* create the display structures that will not change: */ InitLists(); /* setup the callback routines: */ /* DisplayFunc -- redraw the window */ /* ReshapeFunc -- handle the user resizing the window */ /* KeyboardFunc -- handle a keyboard input */ /* MouseFunc -- handle the mouse button going down or up */ /* MotionFunc -- handle the mouse moving with a button down */ /* PassiveMotionFunc -- handle the mouse moving with a button up*/ /* VisibilityFunc -- handle a change in window visibility */ /* EntryFunc -- handle the cursor entering or leaving the window */ /* SpecialFunc -- handle special keys on the keyboard */ /* SpaceballMotionFunc -- handle spaceball translation */ /* SpaceballRotateFunc -- handle spaceball rotation */ /* SpaceballButtonFunc -- handle spaceball button hits */ /* ButtonBoxFunc -- handle button box hits */ /* DialsFunc -- handle dial rotations */ /* TabletMotionFunc -- handle digitizing tablet motion */ /* TabletButtonFunc -- handle digitizing tablet button hits */ /* MenuStateFunc -- declare when a pop-up menu is in use */ /* IdleFunc -- what to do when nothing else is going on */ /* TimerFunc -- trigger something to happen every so often */ glutSetWindow( GraphicsWindow ); glutDisplayFunc( Display ); glutReshapeFunc( Resize ); glutKeyboardFunc( Keyboard ); glutMouseFunc( MouseButton ); glutMotionFunc( MouseMotion ); glutPassiveMotionFunc( NULL ); glutVisibilityFunc( Visibility ); glutEntryFunc( NULL ); glutSpecialFunc( NULL ); glutSpaceballMotionFunc( NULL ); glutSpaceballRotateFunc( NULL ); glutSpaceballButtonFunc( NULL ); glutButtonBoxFunc( NULL ); glutDialsFunc( NULL ); glutTabletMotionFunc( NULL ); glutTabletButtonFunc( NULL ); glutMenuStateFunc( NULL ); // DO NOT SET THE GLUT IDLE FUNCTION HERE !! // glutIdleFunc( NULL ); // let glui take care of it in InitGlui() glutTimerFunc( 0, NULL, 0 ); /* open the lut window and set its title: */ glutInitWindowSize( LUTWIN_XSIZE, LUTWIN_YSIZE ); glutInitWindowPosition( LUTWIN_LEFT, LUTWIN_TOP ); LutWindow = glutCreateWindow( LUTWINDOWTITLE ); glutSetWindowTitle( LUTWINDOWTITLE ); glClearColor( BGCOLOR[0], BGCOLOR[1], BGCOLOR[2], BGCOLOR[3] ); /* setup the callback routines: */ glutSetWindow( LutWindow ); glutDisplayFunc( LutDisplay ); glutReshapeFunc( LutResize ); glutKeyboardFunc( Keyboard ); glutMouseFunc( LutMouseButton ); glutMotionFunc( LutMouseMotion ); glutPassiveMotionFunc( NULL ); glutVisibilityFunc( LutVisibility ); glutEntryFunc( NULL ); glutSpecialFunc( NULL ); glutSpaceballMotionFunc( NULL ); glutSpaceballRotateFunc( NULL ); glutSpaceballButtonFunc( NULL ); glutButtonBoxFunc( NULL ); glutDialsFunc( NULL ); glutTabletMotionFunc( NULL ); glutTabletButtonFunc( NULL ); glutMenuStateFunc( NULL ); // DO NOT SET THE GLUT IDLE FUNCTION HERE !! // glutIdleFunc( NULL ); // let glui take care of it in InitGlui() glutTimerFunc( 0, NULL, 0 ); } /** ** initialize the histogram data structure and display list: **/ void InitHistogram( void ) { int i; /* counter */ int nv; /* # voxels in volume */ struct bucket *b; /* points into Buckets array */ unsigned int sx, sy; unsigned char *array8; unsigned short *array12; /* init the Buckets array: */ Buckets = (struct bucket *) malloc( LutSize * sizeof(struct bucket) ); for( i = 0, b = Buckets; i < LutSize; i++, b++ ) { b->n = 0; } /* map the volume, and loop through it, counting occurances: */ nv = VolNx * VolNy * VolNz; for( i = 0; i < NumVolumes; i++ ) { /* map the volume and count the entries: */ if( LutSize == 256 ) { Volumes[ State.NowVolume ].VolData->MapVolume( kVLIAccessReadExclusive, (void *&)array8, sx, sy ); for( i = 0; i < nv; i++ ) { Buckets[ array8[i] ].n++; } } else { Volumes[ State.NowVolume ].VolData->MapVolume( kVLIAccessReadExclusive, (void *&)array12, sx, sy ); for( i = 0; i < nv; i++ ) { Buckets[ array12[i] ].n++; } } /* unmap the volume: */ Volumes[ State.NowVolume ].VolData->UnmapVolume(); } /* compute the logs: */ HistoMax = 0.; for( i = 0, b = Buckets; i < LutSize; i++, b++ ) { if( b->n <= 1 ) b->logn = 0.; else { b->logn = log( (double)b->n ); if( b->logn > HistoMax ) HistoMax = b->logn; } } /* initialize an empty histogram display list: */ glutSetWindow( LutWindow ); HistogramDL = glGenLists( 1 ); glNewList( HistogramDL, GL_COMPILE ); glEndList(); /* make the initial histogram display list: */ #ifdef NOTYET CreateHistogram(); #endif } /** ** initialize the display lists that will not change: **/ void InitLists( void ) { if( Verbose ) fprintf( stderr, "InitLists\n" ); AxesDL = glGenLists( 1 ); glNewList( AxesDL, GL_COMPILE ); glColor3fv( AXES_COLOR ); glLineWidth( AXES_WIDTH ); VolAxes(); glLineWidth( 1. ); glEndList(); } /** ** initialize the user interface window: **/ void InitUi() { char str[256]; /* sprintf buffer */ int i; /* counter */ GLUI_HSlider *slider; GLUI_Panel *panel, *bigpanel, *framespanel; GLUI_RadioGroup *group; if( Verbose ) fprintf( stderr, "InitUi\n" ); /* setup the glui window: */ glutInitWindowPosition( UIWIN_LEFT, UIWIN_TOP ); Glui = GLUI_Master.create_glui( (char *) UIWINDOWTITLE ); Glui->add_statictext( (char *) UIWINDOWTITLE ); Glui->add_separator(); /* axes, bounding box, and negative: */ bigpanel = Glui->add_panel( "", GLUI_PANEL_NONE ); panel = Glui->add_panel_to_panel( bigpanel, "" ); Glui->add_checkbox_to_panel( panel, "Axes", &State.AxesOn ); Glui->add_checkbox_to_panel( panel, "Bounding Box", &State.BoundingBoxOn ); /* which stereo view to use: */ Glui->add_column_to_panel( bigpanel, GLUIFALSE ); panel = Glui->add_panel_to_panel( bigpanel, "Stereo View" ); group = Glui->add_radiogroup_to_panel( panel, &State.StereoView ); Glui->add_radiobutton_to_group( group, "Left" ); Glui->add_radiobutton_to_group( group, "Middle" ); Glui->add_radiobutton_to_group( group, "Right" ); /* transfer function band: */ panel = Glui->add_panel( "Band Selection" ); group = Glui->add_radiogroup_to_panel( panel, &State.Band, REDOALT, (GLUI_Update_CB) Buttons ); Glui->add_radiobutton_to_group( group, "Band Pass" ); Glui->add_radiobutton_to_group( group, "Band Reject" ); Glui->add_column_to_panel( panel, FALSE ); slider = Glui->add_slider_to_panel( panel, GLUITRUE, GLUI_HSLIDER_INT, &State.VolMin, VOL, (GLUI_Update_CB) Sliders ); slider->set_w( SLIDERWIDTH ); slider->set_int_limits( VOLMIN, VOLMAX ); slider->set_slider_val( State.VolMin, State.VolMax ); sprintf( str, VOLFORMAT, State.VolMin, State.VolMax ); VolLabel = Glui->add_statictext_to_panel( panel, str ); slider = Glui->add_slider_to_panel( panel, GLUIFALSE, GLUI_HSLIDER_FLOAT, &State.MaxAlpha, MAXALPHA, (GLUI_Update_CB) Sliders ); slider->set_w( SLIDERWIDTH ); slider->set_float_limits( MAXALPHAMIN, MAXALPHAMAX ); sprintf( str, MAXALPHAFORMAT, State.MaxAlpha ); MaxAlphaLabel = Glui->add_statictext_to_panel( panel, str ); slider = Glui->add_slider_to_panel( panel, GLUIFALSE, GLUI_HSLIDER_INT, &State.BlendRegion, BLENDREGION, (GLUI_Update_CB) Sliders ); slider->set_w( SLIDERWIDTH ); slider->set_int_limits( BLENDREGIONMIN, BLENDREGIONMAX ); sprintf( str, BLENDREGIONFORMAT, State.BlendRegion ); BlendRegionLabel = Glui->add_statictext_to_panel( panel, str ); /* cropping sliders: */ bigpanel = Glui->add_panel( "Cutting Planes" ); panel = Glui->add_panel_to_panel( bigpanel, "Orthogonal Cropping" ); slider = Glui->add_slider_to_panel( panel, GLUITRUE, GLUI_HSLIDER_INT, &State.VolXCropMin, VOLXCROP, (GLUI_Update_CB) Sliders ); slider->set_w( SLIDERWIDTH ); slider->set_int_limits( VOLXCROPMIN, VOLXCROPMAX ); sprintf( str, VOLXCROPFORMAT, State.VolXCropMin, State.VolXCropMax ); VolXCropLabel = Glui->add_statictext_to_panel( panel, str ); slider = Glui->add_slider_to_panel( panel, GLUITRUE, GLUI_HSLIDER_INT, &State.VolYCropMin, VOLYCROP, (GLUI_Update_CB) Sliders ); slider->set_w( SLIDERWIDTH ); slider->set_int_limits( VOLYCROPMIN, VOLYCROPMAX ); sprintf( str, VOLYCROPFORMAT, State.VolYCropMin, State.VolYCropMax ); VolYCropLabel = Glui->add_statictext_to_panel( panel, str ); slider = Glui->add_slider_to_panel( panel, GLUITRUE, GLUI_HSLIDER_INT, &State.VolZCropMin, VOLZCROP, (GLUI_Update_CB) Sliders ); slider->set_w( SLIDERWIDTH ); slider->set_int_limits( VOLZCROPMIN, VOLZCROPMAX ); sprintf( str, VOLZCROPFORMAT, State.VolZCropMin, State.VolZCropMax ); VolZCropLabel = Glui->add_statictext_to_panel( panel, str ); Glui->add_column_to_panel( bigpanel, GLUIFALSE ); /* arbitrary cutting plane: */ panel = Glui->add_panel_to_panel( bigpanel, "Arbitrary Cutting Plane" ); Glui->add_checkbox_to_panel( panel, "Arbitrary Cutting Plane", &State.CutPlaneOn, CUTTINGPLANE, (GLUI_Update_CB) Buttons ); slider = Glui->add_slider_to_panel( panel, GLUIFALSE, GLUI_HSLIDER_FLOAT, &State.CutD, CUTPLANE, (GLUI_Update_CB) Sliders ); slider->set_w( SLIDERWIDTH ); slider->set_float_limits( CutPlaneMin, CutPlaneMax ); sprintf( str, CUTPLANEFORMAT, -State.CutD ); CutPlaneLabel = Glui->add_statictext_to_panel( panel, str ); /* which channel to sculpt: */ #ifdef NOTDEF panel = Glui->add_panel( "Channel to Sculpt" ); group = Glui->add_radiogroup_to_panel( panel, &CurrentChannel ); Glui->add_radiobutton_to_group( group, "Red" ); Glui->add_radiobutton_to_group( group, "Green" ); Glui->add_radiobutton_to_group( group, "Blue" ); Glui->add_radiobutton_to_group( group, "Alpha" ); #endif /* modify the clt: */ panel = Glui->add_panel( "Modify the CLT" ); Glui->add_checkbox_to_panel( panel, "Negative", &State.Negative, REDOCLT, (GLUI_Update_CB) Buttons ); slider = Glui->add_slider_to_panel( panel, GLUIFALSE, GLUI_HSLIDER_INT, &State.Roll, ROLL, (GLUI_Update_CB) Sliders ); slider->set_w( SLIDERWIDTH ); slider->set_int_limits( 0, LutSize-1 ); sprintf( str, ROLLFORMAT, State.Roll ); RollLabel = Glui->add_statictext_to_panel( panel, str ); /* animation: */ /* frame number if read more than 1 volume: */ if( NumVolumes > 1 ) { panel = Glui->add_panel( "Which Volume" ); VolFileList = Glui->add_listbox_to_panel( panel, "Volume File", &State.NowVolume, VOLUMEFILE, (GLUI_Update_CB) Buttons ); for( i = 0; i < NumVolumes; i++ ) { VolFileList->add_item( i, Volumes[i].VolumeFilename ); } VolFileList->set_int_val( State.NowVolume ); VolFileList->set_alignment( GLUI_ALIGN_RIGHT ); slider = Glui->add_slider_to_panel( panel, GLUIFALSE, GLUI_HSLIDER_INT, &State.NowVolume, NOWVOLUME, (GLUI_Update_CB) Sliders ); slider->set_w( SLIDERWIDTH ); slider->set_int_limits( 0, NumVolumes-1 ); sprintf( str, VOLNUMBERFORMAT, State.NowVolume ); VolNumberLabel = Glui->add_statictext_to_panel( panel, str ); } bigpanel = Glui->add_panel( "Animation" ); framespanel = Glui->add_panel_to_panel( bigpanel, "", GLUIFALSE ); panel = Glui->add_panel_to_panel( framespanel, "KeyFrame 0" ); Glui->add_button_to_panel( panel, "Record", RECORD0, (GLUI_Update_CB) Buttons ); Glui->add_column_to_panel( panel, FALSE ); Glui->add_button_to_panel( panel, "Go To", GOTO0, (GLUI_Update_CB) Buttons ); Glui->add_column_to_panel( framespanel, FALSE ); panel = Glui->add_panel_to_panel( framespanel, "KeyFrame 1" ); Glui->add_button_to_panel( panel, "Record", RECORD1, (GLUI_Update_CB) Buttons ); Glui->add_column_to_panel( panel, FALSE ); Glui->add_button_to_panel( panel, "Go To", GOTO1, (GLUI_Update_CB) Buttons ); group = Glui->add_radiogroup_to_panel( bigpanel, &AnimMode, ANIM, (GLUI_Update_CB) Buttons ); Glui->add_radiobutton_to_group( group, "Off" ); Glui->add_radiobutton_to_group( group, "Forward-Forward" ); Glui->add_radiobutton_to_group( group, "Reverse-Reverse" ); Glui->add_radiobutton_to_group( group, "Forward-Reverse" ); slider = Glui->add_slider_to_panel( bigpanel, GLUIFALSE, GLUI_HSLIDER_INT, &AnimFrames, ANIMFRAMES, (GLUI_Update_CB) Sliders ); slider->set_w( SLIDERWIDTH ); slider->set_int_limits( MINANIMFRAMES, MAXANIMFRAMES ); sprintf( str, ANIMFRAMESFORMAT, AnimFrames ); AnimFramesLabel = Glui->add_statictext_to_panel( bigpanel, str ); slider = Glui->add_slider_to_panel( bigpanel, GLUIFALSE, GLUI_HSLIDER_FLOAT, &AnimT, ANIMT, (GLUI_Update_CB) Sliders ); slider->set_w( SLIDERWIDTH ); slider->set_float_limits( 0., 1. ); sprintf( str, ANIMTFORMAT, (int)( AnimFrames*AnimT) ); AnimTLabel = Glui->add_statictext_to_panel( bigpanel, str ); /* what to rotate: */ panel = Glui->add_panel( "What to Rotate" ); group = Glui->add_radiogroup_to_panel( panel, &WhichRotate ); Glui->add_radiobutton_to_group( group, "Volume Only" ); Glui->add_radiobutton_to_group( group, "Volume + Light" ); Glui->add_radiobutton_to_group( group, "Light Only" ); Glui->add_radiobutton_to_group( group, "Cutting Plane" ); /* scene lighting: */ panel = Glui->add_panel( "Scene Lighting" ); slider = Glui->add_slider_to_panel( panel, GLUIFALSE, GLUI_HSLIDER_FLOAT, &State.EmissionLightModel, 0, (GLUI_Update_CB) SetLightModel ); slider->set_w( SLIDERWIDTH ); slider->set_float_limits( 0., 1. ); sprintf( str, EMISSIONFORMAT, (int)(100.*1.), (int)(100.*1.) ); EmmissionLabel = Glui->add_statictext_to_panel( panel, str ); slider = Glui->add_slider_to_panel( panel, GLUIFALSE, GLUI_HSLIDER_FLOAT, &State.DiffuseSpecular, 0, (GLUI_Update_CB) SetLightModel ); slider->set_w( SLIDERWIDTH ); slider->set_float_limits( 0., 1. ); sprintf( str, DIFFUSEFORMAT, (int)(100.*1.), (int)(100.*1.) ); DiffuseLabel = Glui->add_statictext_to_panel( panel, str ); /* canned color transfer functions: */ panel = Glui->add_panel( "Set A Pre-defined Transfer Function" ); Glui->add_button_to_panel( panel, "Grayscale", GRAYSCALE, (GLUI_Update_CB) Buttons ); Glui->add_column_to_panel( panel, FALSE ); Glui->add_button_to_panel( panel, "Rainbow", RAINBOW, (GLUI_Update_CB) Buttons ); Glui->add_column_to_panel( panel, FALSE ); Glui->add_button_to_panel( panel, "Heated Object", HEATEDOBJ, (GLUI_Update_CB) Buttons ); Glui->add_column_to_panel( panel, FALSE ); Glui->add_button_to_panel( panel, "Brown-Green", BROWNGREEN, (GLUI_Update_CB) Buttons ); /* cannot be called until *both* EmissionLabel and DiffuseLabel are setup: */ SetLightModel(); #ifdef NOTDEF /* transformations: */ GLUI_Translation *trans, *scale; panel = Glui->add_panel( "Object Transformation" ); Glui->add_rotation_to_panel( panel, "Rotation", (float *) RotMatrix ); Glui->add_column_to_panel( panel, FALSE ); scale = Glui->add_translation_to_panel( panel, "Scale", GLUI_TRANSLATION_Y , &State.Scale2 ); scale->set_speed( 0.01f ); Glui->add_column_to_panel( panel, FALSE ); trans = Glui->add_translation_to_panel( panel, "Trans XY", GLUI_TRANSLATION_XY, &TransXYZ[0] ); trans->set_speed( 0.1f ); Glui->add_column_to_panel( panel, FALSE ); trans = Glui->add_translation_to_panel( panel, "Trans Z", GLUI_TRANSLATION_Z , &TransXYZ[2] ); trans->set_speed( 0.1f ); #endif /* items at the bottom: */ panel = Glui->add_panel( "", FALSE ); Glui->add_button_to_panel( panel, "Save As...", 0, (GLUI_Update_CB) SaveFileDialog ); Glui->add_column_to_panel( panel, FALSE ); Glui->add_button_to_panel( panel, "Reset", RESET, (GLUI_Update_CB) Buttons ); Glui->add_column_to_panel( panel, FALSE ); Glui->add_button_to_panel( panel, "Quit", QUIT, (GLUI_Update_CB) Buttons ); Glui->add_column_to_panel( panel, FALSE ); Glui->add_checkbox_to_panel( panel, "Verbose", &Verbose ); /* tell glui what graphics window it needs to post a redisplay to: */ Glui->set_main_gfx_window( GraphicsWindow ); /* set the graphics window's idle function: */ GLUI_Master.set_glutIdleFunc( NULL ); } /** ** init the volume board and the board's software: **/ int InitVolumeBoard( void ) { VLIStatus status; if( Verbose ) fprintf( stderr, "InitVolume\n" ); /* open the VP500 board: */ status = VLIOpen(); if( status != kVLIOK ) { fprintf( stderr, "InitVolume(): %s\n", VLIUtGetErrorString( status ) ); return CANTINIT; } VolContext = VLIContext::Create(); return OK; } /** ** init the volume board information based on the volumes we have read: **/ int InitVolumeInfo( void ) { VLIStatus status; int i; /* counter */ enum VLILookupTable::Size size; /* how big does lut need to be */ float denom; /* unitize cut plane normal */ VLIVector3D vector; /* to set light direction */ if( Verbose ) fprintf( stderr, "InitVolumeInfo()\n" ); /* set the global variables that describe the volume: */ /* these will be used by InitUi() and others to work */ /* with volume editing: */ State.VolMin = 1; State.VolMax = LutSize - 1; State.VolXCropMin = 0; State.VolXCropMax = VolNx; State.VolYCropMin = 0; State.VolYCropMax = VolNy; State.VolZCropMin = 0; State.VolZCropMax = VolNz; State.MaxAlpha = ORIGMAXALPHA; State.BlendRegion = ORIGBLENDREGION; /* empty the lut sculpting points: */ RedPts = GreenPts = BluePts = AlphaPts = NULL; /* init the lookup table and set it to all opaque: */ switch( LutSize ) { case 256: size = VLILookupTable::kSize256; break; case 4096: size = VLILookupTable::kSize4096; break; } VolLUT = VLILookupTable::Create( size ); status = VolContext->SetLookupTable( VolLUT ); if( status != kVLIOK ) { fprintf( stderr, "InitVolume(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } for( i = 0; i < LutSize; i++ ) { status = VolLUT->SetAlphaEntry( i, ORIGMAXALPHA ); if( status != kVLIOK ) { fprintf( stderr, "SetAlphaEntry(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } /* create an initial color range:: */ SetColorRange( HEATEDOBJ ); CurrentChannel = ALPHA; SetAlphaRange( SLIDER ); for( i = 0; i < LutSize; i++ ) { State0.LutRed[i] = State.LutRed[i]; State0.LutGreen[i] = State.LutGreen[i]; State0.LutBlue[i] = State.LutBlue[i]; State1.LutRed[i] = State.LutRed[i]; State1.LutGreen[i] = State.LutGreen[i]; State1.LutBlue[i] = State.LutBlue[i]; } /* now see if we need to load an external State from a file: */ StateFilename = NULL; GetStateFilename(); /* setup the cutting plane: */ denom = sqrt( Sqr(CUTA) + Sqr(CUTB) + Sqr(CUTC) ); State.CutA = CUTA/denom; State.CutB = CUTB/denom; State.CutC = CUTC/denom; State.CutD = CUTD; CutThick = sqrt( VolNx*VolNx + VolNy*VolNy + VolNz*VolNz ); State.CutD = -(float)VolNy; VolCutPlane = VLICutPlane::Create( State.CutA, State.CutB, State.CutC, State.CutD, CutThick, 0., VLICutPlane::kInside ); State.CutPlaneOn = OFF; CutPlaneMin = -sqrt( VolNx*VolNx + VolNy*VolNy + VolNz*VolNz ); CutPlaneMax = 0.; /* setup the directional light source: */ VolLight = VLILight::CreateDirectional(); VolContext->AddLight( VolLight ); vector.X() = LIGHTVX; vector.Y() = LIGHTVY; vector.Z() = LIGHTVZ; status = VolLight->SetDirection( vector ); if( status != kVLIOK ) { fprintf( stderr, "SetDirection(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } State.EmissionLightModel = 0.50; State.DiffuseSpecular = 0.25; /* init the volume model matrices to identity: */ for( i = 0; i < NumVolumes; i++ ) { status = Volumes[i].VolData->SetModelMatrix( VolMatrix ); if( status != kVLIOK ) { fprintf( stderr, "SetModelMatrix(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } status = VolContext->SetBasePlaneFormat( kVLIPixelFormatRGBA8 ); if( status != kVLIOK ) { fprintf( stderr, "InitVolume(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolCrop.SetXSlab( State.VolXCropMin, State.VolXCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetXSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolCrop.SetYSlab( State.VolYCropMin, State.VolYCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetYSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolCrop.SetZSlab( State.VolZCropMin, State.VolZCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetZSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolCrop.SetFlags( VLICrop::kSubVolume ); if( status != kVLIOK ) { fprintf( stderr, "SetFlags(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->SetCrop( VolCrop ); if( status != kVLIOK ) { fprintf( stderr, "SetCrop(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } return OK; } /** ** insert an RGB color into the color lists: **/ void InsertColor( float s, float r, float g, float b ) { int temp; /* save CurrentChannel */ temp = CurrentChannel; CurrentChannel = RED; InsertPoint( s, r ); CurrentChannel = GREEN; InsertPoint( s, g ); CurrentChannel = BLUE; InsertPoint( s, b ); CurrentChannel = temp; } /** ** insert the given (s,v) pair into the current channel's curve list: **/ struct scpt * InsertPoint( float s, float v ) { struct scpt *p; /* in the list */ struct scpt *pt; /* the new point */ struct scpt **head; /* address of the head of the */ /* current channel's list */ /* find start of list: */ switch( CurrentChannel ) { case RED: head = &RedPts; break; case GREEN: head = &GreenPts; break; case BLUE: head = &BluePts; break; case ALPHA: head = &AlphaPts; break; } /* create the new point: */ pt = new struct scpt; pt->s = s; pt->v = v; /* if list is empty, then sole member will be this point: */ p = *head; /* first element of the list */ if( p == NULL ) { pt->next = NULL; pt->prev = NULL; *head = pt; /* assign list head */ return pt; } /* if list is not empty, find point before this new point in s order: */ /* assume first point will always have s == 0 */ for( p = *head; p->next != NULL && p->next->s < s; p = p->next ) ; /* insert after here: */ pt->next = p->next; if( pt->next != NULL ) pt->next->prev = pt; pt->prev = p; p->next = pt; return pt; } /** ** interpolate State0 into State1: ** ** 0. <= t <= 1. ** **/ void InterpolateState( float t ) { int i; /* lut counter */ float x1, y1, z1; /* temporary vector */ float c, s; /* cosine, sine */ float denom; /* re-unitize cut plane normal */ VLIVector3D vector; /* to set light direction */ VLIStatus status; char str[256]; /* sprintf string */ /* be sure t is in range: */ if( t < 0. ) t = 0.; if( t > 1. ) t = 1.; /* interpolate all variables in the state vector: */ if( State0.AxesOn != State1.AxesOn ) { State.AxesOn = Lerp( t, State0.AxesOn, State1.AxesOn ); } if( State0.Band != State1.Band ) { State.Band = Lerp( t, State0.Band, State1.Band ); SetAlphaRange( SLIDER ); } if( State0.BlendRegion != State1.BlendRegion ) { State.BlendRegion = Lerp( t, State0.BlendRegion, State1.BlendRegion ); SetAlphaRange( SLIDER ); } if( State0.BoundingBoxOn != State1.BoundingBoxOn ) { State.BoundingBoxOn = Lerp( t, State0.BoundingBoxOn, State1.BoundingBoxOn ); } if( State0.CutA != State1.CutA ) { State.CutA = Lerp( t, State0.CutA, State1.CutA ); status = VolCutPlane->SetPlane( State.CutA, State.CutB, State.CutC, State.CutD ); if( status != kVLIOK ) { fprintf( stderr, "SetPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->RemoveCutPlane( VolCutPlane ); if( State.CutPlaneOn ) { status = VolContext->AddCutPlane( VolCutPlane ); if( status != kVLIOK ) { fprintf( stderr, "AddCutPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } } if( State0.CutB != State1.CutB ) { State.CutB = Lerp( t, State0.CutB, State1.CutB ); status = VolCutPlane->SetPlane( State.CutA, State.CutB, State.CutC, State.CutD ); if( status != kVLIOK ) { fprintf( stderr, "SetPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->RemoveCutPlane( VolCutPlane ); if( State.CutPlaneOn ) { status = VolContext->AddCutPlane( VolCutPlane ); if( status != kVLIOK ) { fprintf( stderr, "AddCutPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } } if( State0.CutC != State1.CutC ) { State.CutC = Lerp( t, State0.CutC, State1.CutC ); status = VolCutPlane->SetPlane( State.CutA, State.CutB, State.CutC, State.CutD ); if( status != kVLIOK ) { fprintf( stderr, "SetPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->RemoveCutPlane( VolCutPlane ); if( State.CutPlaneOn ) { status = VolContext->AddCutPlane( VolCutPlane ); if( status != kVLIOK ) { fprintf( stderr, "AddCutPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } } if( State0.CutD != State1.CutD ) { State.CutD = Lerp( t, State0.CutD, State1.CutD ); status = VolCutPlane->SetPlane( State.CutA, State.CutB, State.CutC, State.CutD ); if( status != kVLIOK ) { fprintf( stderr, "SetPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->RemoveCutPlane( VolCutPlane ); if( State.CutPlaneOn ) { status = VolContext->AddCutPlane( VolCutPlane ); if( status != kVLIOK ) { fprintf( stderr, "AddCutPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } } if( State0.CutPlaneOn != State1.CutPlaneOn ) { State.CutPlaneOn = Lerp( t, State0.CutPlaneOn, State1.CutPlaneOn ); status = VolContext->RemoveCutPlane( VolCutPlane ); if( State.CutPlaneOn ) { status = VolContext->AddCutPlane( VolCutPlane ); if( status != kVLIOK ) { fprintf( stderr, "AddCutPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } } if( State0.DiffuseSpecular != State1.DiffuseSpecular ) { State.DiffuseSpecular = Lerp( t, State0.DiffuseSpecular, State1.DiffuseSpecular ); SetLightModel(); } if( State0.EmissionLightModel != State1.EmissionLightModel ) { State.EmissionLightModel = Lerp( t, State0.EmissionLightModel, State1.EmissionLightModel ); SetLightModel(); } /* should we test to see if we really need to do this? */ for( i = 0; i < LutSize; i++ ) { State.LutRed[i] = Lerp( t, State0.LutRed[i], State1.LutRed[i] ); State.LutGreen[i] = Lerp( t, State0.LutGreen[i], State1.LutGreen[i] ); State.LutBlue[i] = Lerp( t, State0.LutBlue[i], State1.LutBlue[i] ); } SetCLT(); if( State0.MaxAlpha != State1.MaxAlpha ) { State.MaxAlpha = Lerp( t, State0.MaxAlpha, State1.MaxAlpha ); SetAlphaRange( SLIDER ); } if( State0.Negative != State1.Negative ) { State.Negative = Lerp( t, State0.Negative, State1.Negative ); SetCLT(); } if( State0.NowVolume != State1.NowVolume ) { State.NowVolume = Lerp( t, State0.NowVolume, State1.NowVolume ); SetCLT(); VolFileList->set_int_val( State.NowVolume ); sprintf( str, VOLNUMBERFORMAT, State.NowVolume ); VolNumberLabel->set_text( str ); } if( State0.Projection != State1.Projection ) { State.Projection = Lerp( t, State0.Projection, State1.Projection ); } if( State0.Roll != State1.Roll ) { State.Roll = Lerp( t, State0.Roll, State1.Roll ); SetCLT(); } if( State0.Scale != State1.Scale ) { State.Scale = Lerp( t, State0.Scale, State1.Scale ); } if( State0.StereoView != State1.StereoView ) { State.StereoView = Lerp( t, State0.StereoView, State1.StereoView ); switch( State.StereoView ) { case STEREOLEFT: State.StereoAng = STEREOANG; break; case STEREOMIDDLE: State.StereoAng = 0.; break; case STEREORIGHT: State.StereoAng = -STEREOANG; break; } VLIMatrix tmatp = VLIMatrix::Translate( (double)VolNx/2., (double)VolNy/2., (double)VolNz/2. ); VLIMatrix xmat = VLIMatrix::Rotate( State.Xang, Xaxis ); VLIMatrix ymat = VLIMatrix::Rotate( State.Yang+State.StereoAng, Yaxis ); VLIMatrix tmatm = VLIMatrix::Translate( -(double)VolNx/2., -(double)VolNy/2., -(double)VolNz/2. ); VLIMatrix mat = VLIMatrix::Identity(); mat *= tmatp; mat *= ymat; mat *= xmat; mat *= tmatm; status = VolContext->GetCamera().SetViewMatrix( mat ); if( status != kVLIOK ) { fprintf( stderr, "Keyboard(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } if( State0.VolMin != State1.VolMin ) { State.VolMin = Lerp( t, State0.VolMin, State1.VolMin ); SetAlphaRange( SLIDER ); } if( State0.VolMax != State1.VolMax ) { State.VolMax = Lerp( t, State0.VolMax, State1.VolMax ); SetAlphaRange( SLIDER ); } if( State0.VolXCropMin != State1.VolXCropMin ) { State.VolXCropMin = Lerp( t, State0.VolXCropMin, State1.VolXCropMin ); status = VolCrop.SetXSlab( State.VolXCropMin, State.VolXCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetXSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->SetCrop( VolCrop ); if( status != kVLIOK ) { fprintf( stderr, "SetCrop(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } if( State0.VolXCropMax != State1.VolXCropMax ) { State.VolXCropMax = Lerp( t, State0.VolXCropMax, State1.VolXCropMax ); status = VolCrop.SetXSlab( State.VolXCropMin, State.VolXCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetXSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->SetCrop( VolCrop ); if( status != kVLIOK ) { fprintf( stderr, "SetCrop(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } if( State0.VolYCropMin != State1.VolYCropMin ) { State.VolYCropMin = Lerp( t, State0.VolYCropMin, State1.VolYCropMin ); status = VolCrop.SetYSlab( State.VolYCropMin, State.VolYCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetYSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->SetCrop( VolCrop ); if( status != kVLIOK ) { fprintf( stderr, "SetCrop(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } if( State0.VolYCropMax != State1.VolYCropMax ) { State.VolYCropMax = Lerp( t, State0.VolYCropMax, State1.VolYCropMax ); status = VolCrop.SetYSlab( State.VolYCropMin, State.VolYCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetYSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->SetCrop( VolCrop ); if( status != kVLIOK ) { fprintf( stderr, "SetCrop(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } if( State0.VolZCropMin != State1.VolZCropMin ) { State.VolZCropMin = Lerp( t, State0.VolZCropMin, State1.VolZCropMin ); status = VolCrop.SetZSlab( State.VolZCropMin, State.VolZCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetZSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->SetCrop( VolCrop ); if( status != kVLIOK ) { fprintf( stderr, "SetCrop(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } if( State0.VolZCropMax != State1.VolZCropMax ) { State.VolZCropMax = Lerp( t, State0.VolZCropMax, State1.VolZCropMax ); status = VolCrop.SetZSlab( State.VolZCropMin, State.VolZCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetZSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->SetCrop( VolCrop ); if( status != kVLIOK ) { fprintf( stderr, "SetCrop(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } if( State0.Xang != State1.Xang ) { State.Xang = Lerp( t, State0.Xang, State1.Xang ); ActiveButton = LEFT; MouseMotion( 0, 0 ); ActiveButton = 0; } if( State0.Yang != State1.Yang ) { State.Yang = Lerp( t, State0.Yang, State1.Yang ); ActiveButton = LEFT; MouseMotion( 0, 0 ); ActiveButton = 0; } if( State0.XangCut != State1.XangCut || State0.YangCut != State1.YangCut ) { State.XangCut = Lerp( t, State0.XangCut, State1.XangCut ); State.YangCut = Lerp( t, State0.YangCut, State1.YangCut ); c = cos( M_PI*State.XangCut/180. ); s = sin( M_PI*State.XangCut/180. ); x1 = CUTA; y1 = c * CUTB - s * CUTC; z1 = s * CUTB + c * CUTC; c = cos( M_PI*State.YangCut/180. ); s = sin( M_PI*State.YangCut/180. ); State.CutA = c * x1 + s * z1; State.CutB = y1; State.CutC = -s * x1 + c * z1; denom = sqrt( Sqr(State.CutA) + Sqr(State.CutB) + Sqr(State.CutC) ); State.CutA /= denom; State.CutB /= denom; State.CutC /= denom; /* fprintf( stderr, "CutPlane = %6.2f, %6.2f, %6.2f, %6.2f\n", CutA, CutB, CutC, CutD ); */ status = VolCutPlane->SetPlane( State.CutA, State.CutB, State.CutC, State.CutD ); if( status != kVLIOK ) { fprintf( stderr, "SetPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } if( State0.XangLight != State1.XangLight || State0.YangLight != State1.YangLight ) { State.XangLight = Lerp( t, State0.XangLight, State1.XangLight ); State.YangLight = Lerp( t, State0.YangLight, State1.YangLight ); c = cos( M_PI*State.XangLight/180. ); s = sin( M_PI*State.XangLight/180. ); x1 = LIGHTVX; y1 = c * LIGHTVY - s * LIGHTVZ; z1 = s * LIGHTVY + c * LIGHTVZ; c = cos( M_PI*State.YangLight/180. ); s = sin( M_PI*State.YangLight/180. ); vector.X() = c * x1 + s * z1; vector.Y() = y1; vector.Z() = -s * x1 + c * z1; status = VolLight->SetDirection( vector ); if( status != kVLIOK ) { fprintf( stderr, "SetDirection(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } /* synchronize the GLUI display with the variables: */ Glui->sync_live(); /* be sure the graphics windows get redrawn: */ glutSetWindow( LutWindow ); glutPostRedisplay(); glutSetWindow( GraphicsWindow ); glutPostRedisplay(); } /** ** react to a keyboard hit: **/ void Keyboard( unsigned char c, int x, int y ) { VLIStatus status; if( Verbose ) fprintf( stderr, "Keyboard: '%c' (0x%0x)\n", c, c ); switch( c ) { #ifdef NOTDEF case '1': status = VolContext->SetSuperSamplingFactor( 1., 1., 1. ); if( status != kVLIOK ) { fprintf( stderr, "Keyboard(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } break; case '2': status = VolContext->SetSuperSamplingFactor( .5, .5, .5 ); if( status != kVLIOK ) { fprintf( stderr, "Keyboard(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } break; case 'c': status = VolContext->SetSuperSamplingSpace( kVLICameraSpace ); if( status != kVLIOK ) { fprintf( stderr, "Keyboard(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } break; case 'o': status = VolContext->SetSuperSamplingSpace( kVLIObjectSpace ); if( status != kVLIOK ) { fprintf( stderr, "Keyboard(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } break; #endif case 'a': State.AxesOn = ! State.AxesOn; break; case 'b': State.BoundingBoxOn = ! State.BoundingBoxOn; break; case 'h': case 'H': Help(); break; case 'n': case 'N': State.Negative = ! State.Negative; SetCLT(); break; case 'p': case 'P': State.Projection = PERSP; break; case 'o': case 'O': State.Projection = ORTHO; break; case 'q': case 'Q': case ESCAPE: Quit(); /* will not return here */ case 'L': State.StereoView = STEREOLEFT; break; case 'M': case 'C': State.StereoView = STEREOMIDDLE; break; case 'R': State.StereoView = STEREORIGHT; break; case 'r': TransformMode = ROTATE; break; case 's': TransformMode = SCALE; break; default: fprintf( stderr, "Don't know what to do with keyboard hit: '%c' (0x%0x)\n", c, c ); } switch( c ) { case 'L': case 'M': case 'C': case 'R': switch( State.StereoView ) { case STEREOLEFT: State.StereoAng = STEREOANG; break; case STEREOMIDDLE: State.StereoAng = 0.; break; case STEREORIGHT: State.StereoAng = -STEREOANG; break; } VLIMatrix tmatp = VLIMatrix::Translate( (double)VolNx/2., (double)VolNy/2., (double)VolNz/2. ); VLIMatrix xmat = VLIMatrix::Rotate( State.Xang, Xaxis ); VLIMatrix ymat = VLIMatrix::Rotate( State.Yang + State.StereoAng, Yaxis ); VLIMatrix tmatm = VLIMatrix::Translate( -(double)VolNx/2., -(double)VolNy/2., -(double)VolNz/2. ); VLIMatrix mat = VLIMatrix::Identity(); mat *= tmatp; mat *= ymat; mat *= xmat; mat *= tmatm; status = VolContext->GetCamera().SetViewMatrix( mat ); if( status != kVLIOK ) { fprintf( stderr, "Keyboard(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } break; } /* synchronize the GLUI display with the variables: */ Glui->sync_live(); /* force a call to Display(): */ glutSetWindow( GraphicsWindow ); glutPostRedisplay(); } /** ** draw the complete scene: **/ void LutDisplay( void ) { int dx, dy; /* viewport size */ struct scpt *p; struct scpt *head; /* head of the sculpted point list */ /* set which window we want to do the graphics into: */ glutSetWindow( LutWindow ); /* erase the background: */ glDrawBuffer( GL_BACK ); glClear( GL_COLOR_BUFFER_BIT ); glDisable( GL_DEPTH_TEST ); /* specify shading to be flat: */ glShadeModel( GL_SMOOTH ); /* set the viewport to a square centered in the window: */ dx = glutGet( GLUT_WINDOW_WIDTH ); dy = glutGet( GLUT_WINDOW_HEIGHT ); glViewport( 0, 0, dx, dy ); /* draw the histogram first, then the curves: */ glCallList( HistogramDL ); #define PAD 2. glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluOrtho2D( -PAD, (float)LutSize+PAD, -PAD/100., 1+PAD/100. ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); if( CurrentChannel != RED ) { glLineWidth( 1. ); glColor3f( 1., 0., 0. ); glBegin( GL_LINE_STRIP ); for( p = RedPts; p != NULL; p = p->next ) { glVertex2f( p->s, p->v ); } glEnd(); } if( CurrentChannel != GREEN ) { glLineWidth( 1. ); glColor3f( 0., 1., 0. ); glBegin( GL_LINE_STRIP ); for( p = GreenPts; p != NULL; p = p->next ) { glVertex2f( p->s, p->v+.002 ); } glEnd(); } if( CurrentChannel != BLUE ) { glLineWidth( 1. ); glColor3f( 0., 0., 1. ); glBegin( GL_LINE_STRIP ); for( p = BluePts; p != NULL; p = p->next ) { glVertex2f( p->s, p->v+.004 ); } glEnd(); } if( CurrentChannel != ALPHA ) { glLineWidth( 1. ); glColor3f( 1., 1., 1. ); glBegin( GL_LINE_STRIP ); for( p = AlphaPts; p != NULL; p = p->next ) { glVertex2f( p->s, p->v ); } glEnd(); } /* draw the sculpting points: */ if( CurrentChannel != RED ) { glPointSize( SMALLPOINTS ); glColor3f( 1., 0., 0. ); glBegin( GL_POINTS ); for( p = RedPts; p != NULL; p = p->next ) { glVertex2f( p->s, p->v ); } glEnd(); } if( CurrentChannel != GREEN ) { glPointSize( SMALLPOINTS ); glColor3f( 0., 1., 0. ); glBegin( GL_POINTS ); for( p = GreenPts; p != NULL; p = p->next ) { glVertex2f( p->s, p->v ); } glEnd(); } if( CurrentChannel != BLUE ) { glPointSize( SMALLPOINTS ); glColor3f( 0., 0., 1. ); glBegin( GL_POINTS ); for( p = BluePts; p != NULL; p = p->next ) { glVertex2f( p->s, p->v ); } glEnd(); } if( CurrentChannel != ALPHA ) { glPointSize( SMALLPOINTS ); glColor3f( 1., 1., 1. ); glBegin( GL_POINTS ); for( p = AlphaPts; p != NULL; p = p->next ) { glVertex2f( p->s, p->v ); } glEnd(); } /* draw the selected curve and points on top: */ switch( CurrentChannel ) { case RED: head = RedPts; glColor3f( 1., 0., 0. ); break; case GREEN: head = GreenPts; glColor3f( 0., 1., 0. ); break; case BLUE: head = BluePts; glColor3f( 0., 0., 1. ); break; case ALPHA: head = AlphaPts; glColor3f( 1., 1., 1. ); break; } glLineWidth( WIDE ); glBegin( GL_LINE_STRIP ); for( p = head; p != NULL; p = p->next ) { glVertex2f( p->s, p->v ); } glEnd(); glPointSize( BIGPOINTS ); glBegin( GL_POINTS ); for( p = head; p != NULL; p = p->next ) { glVertex2f( p->s, p->v ); } glEnd(); /* swap the double-buffered framebuffers: */ glutSwapBuffers(); /* be sure the graphics buffer has been sent: */ glFlush(); } /** ** called when the mouse button transitions down or up: **/ void LutMouseButton ( int button, /* GLUT_*_BUTTON */ int state, /* GLUT_UP or GLUT_DOWN */ int x, /* where mouse was when button was hit */ int y /* where mouse was when button was hit */ ) { int b; /* LEFT, MIDDLE, or RIGHT */ int dx, dy; /* viewport size */ float s, v; /* (scalar,value) point picked */ /* get the proper button bit mask: */ switch( button ) { case GLUT_LEFT_BUTTON: b = LEFT; break; case GLUT_MIDDLE_BUTTON: b = MIDDLE; break; case GLUT_RIGHT_BUTTON: b = RIGHT; break; default: b = 0; fprintf( stderr, "Unknown mouse button: %d\n", button ); } /* button down sets the bit, up clears the bit: */ if( state == GLUT_DOWN ) { /* find current window size: */ glutSetWindow( LutWindow ); dx = glutGet( GLUT_WINDOW_WIDTH ); dy = glutGet( GLUT_WINDOW_HEIGHT ); y = dy - y; /* what s and v values are we really pointing at: */ s = (float)x * (float)LutSize / (float)dx; v = (float)y / (float)dy; if( Verbose ) fprintf( stderr, "LutMouseButton: s,v = %6.2f, %6.2f\n", s, v ); /* see if we are pointing to somewhere on the curve: */ CurrentPoint = PickPoint( s, v ); LutXmouse = x; LutYmouse = y; LutActiveButton |= b; /* set the proper bit */ } else { LutActiveButton &= ~b; /* clear the proper bit */ CurrentPoint = NULL; } glutSetWindow( LutWindow ); glutPostRedisplay(); } /** ** called when the mouse moves while a button is down: **/ void LutMouseMotion( int x, int y ) /* x and y are mouse coords */ { int dx, dy; /* viewport size */ float s, v; /* (scalar,value) point picked */ if( LutActiveButton != 0 && CurrentPoint != NULL ) { /* find current window size: */ glutSetWindow( LutWindow ); dx = glutGet( GLUT_WINDOW_WIDTH ); dy = glutGet( GLUT_WINDOW_HEIGHT ); y = dy - y; /* what s and v values are we really pointing at: */ s = (float)x * (float)LutSize / (float)dx; if( CurrentPoint->prev != NULL ) { if( s <= CurrentPoint->prev->s ) s = CurrentPoint->prev->s + 1; } else { if( s < 0. ) s = 0.; } if( CurrentPoint->next != NULL ) { if( s >= CurrentPoint->next->s ) s = CurrentPoint->next->s - 1; } else { if( s >= LutSize ) s = (float)LutSize; } v = (float)y / (float)dy; if( v < 0. ) v = 0.; if( v > 1. ) v = 1.; /* set them: */ /* if this is the first or last point, only set the v: */ if( CurrentPoint->prev != NULL && CurrentPoint->next != NULL ) CurrentPoint->s = s; CurrentPoint->v = v; } if( CurrentChannel == ALPHA ) { SetAlphaRange( CUSTOM ); } else { SetColorRange( CUSTOM ); } LutXmouse = x; /* new current position */ LutYmouse = y; CreateHistogram(); glutSetWindow( LutWindow ); glutPostRedisplay(); glutSetWindow( GraphicsWindow ); glutPostRedisplay(); } /** ** called when user resizes the window: **/ void LutResize ( int width, /* new value for window width */ int height /* new value for window height */ ) { glutSetWindow( LutWindow ); glutPostRedisplay(); } /** ** handle a change to the window's visibility: **/ void LutVisibility ( int state /* GLUT_VISIBLE or GLUT_NOT_VISIBLE */ ) { if( state == GLUT_VISIBLE ) { glutSetWindow( LutWindow ); glutPostRedisplay(); } } /** ** called when the mouse button transitions down or up: **/ void MouseButton ( int button, /* GLUT_*_BUTTON */ int state, /* GLUT_UP or GLUT_DOWN */ int x, /* where mouse was when button was hit */ int y /* where mouse was when button was hit */ ) { int b; /* LEFT, MIDDLE, or RIGHT */ /* get the proper button bit mask: */ switch( button ) { case GLUT_LEFT_BUTTON: b = LEFT; break; case GLUT_MIDDLE_BUTTON: b = MIDDLE; break; case GLUT_RIGHT_BUTTON: b = RIGHT; break; default: b = 0; fprintf( stderr, "Unknown mouse button: %d\n", button ); } /* button down sets the bit, up clears the bit: */ if( state == GLUT_DOWN ) { Xmouse = x; Ymouse = y; ActiveButton |= b; /* set the proper bit */ } else ActiveButton &= ~b; /* clear the proper bit */ } /** ** called when the mouse moves while a button is down: **/ void MouseMotion( int x, int y ) /* x and y are mouse coords */ { int dx, dy; /* change in mouse coordinates */ float x1, y1, z1; /* temporary vector */ float c, s; /* cosine, sine */ float denom; /* re-unitize cut plane normal */ VLIVector3D vector; /* to set light direction */ VLIStatus status; dx = x - Xmouse; /* change in mouse coords */ dy = y - Ymouse; if( ActiveButton & LEFT ) { if( WhichRotate == ROTCUTPLANE ) { State.XangCut += ( ANGFACT*dy ); State.YangCut += ( ANGFACT*dx ); c = cos( M_PI*State.XangCut/180. ); s = sin( M_PI*State.XangCut/180. ); x1 = CUTA; y1 = c * CUTB - s * CUTC; z1 = s * CUTB + c * CUTC; c = cos( M_PI*State.YangCut/180. ); s = sin( M_PI*State.YangCut/180. ); State.CutA = c * x1 + s * z1; State.CutB = y1; State.CutC = -s * x1 + c * z1; denom = sqrt( State.CutA*State.CutA + State.CutB*State.CutB + State.CutC*State.CutC ); State.CutA /= denom; State.CutB /= denom; State.CutC /= denom; /* fprintf( stderr, "CutPlane = %6.2f, %6.2f, %6.2f, %6.2f\n", CutA, CutB, CutC, CutD ); */ status = VolCutPlane->SetPlane( State.CutA, State.CutB, State.CutC, State.CutD ); if( status != kVLIOK ) { fprintf( stderr, "SetPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } else if( WhichRotate == ROTLIGHTONLY ) { State.XangLight -= ( ANGFACT*dy ); State.YangLight += ( ANGFACT*dx ); c = cos( M_PI*State.XangLight/180. ); s = sin( M_PI*State.XangLight/180. ); x1 = LIGHTVX; y1 = c * LIGHTVY - s * LIGHTVZ; z1 = s * LIGHTVY + c * LIGHTVZ; c = cos( M_PI*State.YangLight/180. ); s = sin( M_PI*State.YangLight/180. ); vector.X() = c * x1 + s * z1; vector.Y() = y1; vector.Z() = -s * x1 + c * z1; status = VolLight->SetDirection( vector ); if( status != kVLIOK ) { fprintf( stderr, "SetDirection(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } else if( TransformMode == ROTATE && WhichRotate == ROTVOLUMELIGHT ) { State.Xang += ( ANGFACT*dy ); State.Yang += ( ANGFACT*dx ); VLIMatrix tmatp = VLIMatrix::Translate( (double)VolNx/2., (double)VolNy/2., (double)VolNz/2. ); VLIMatrix xmat = VLIMatrix::Rotate( State.Xang, Xaxis ); VLIMatrix ymat = VLIMatrix::Rotate( State.Yang+State.StereoAng, Yaxis ); VLIMatrix tmatm = VLIMatrix::Translate( -(double)VolNx/2., -(double)VolNy/2., -(double)VolNz/2. ); VLIMatrix mat = VLIMatrix::Identity(); mat *= tmatp; mat *= ymat; mat *= xmat; mat *= tmatm; status = VolContext->GetCamera().SetViewMatrix( mat ); if( status != kVLIOK ) { fprintf( stderr, "SetViewMatrix(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } else if( TransformMode == ROTATE && WhichRotate == ROTVOLUMEONLY ) { State.Xang += ( ANGFACT*dy ); State.Yang += ( ANGFACT*dx ); VLIMatrix tmatp = VLIMatrix::Translate( (double)VolNx/2., (double)VolNy/2., (double)VolNz/2. ); VLIMatrix xmat = VLIMatrix::Rotate( State.Xang, Xaxis ); VLIMatrix ymat = VLIMatrix::Rotate( State.Yang+State.StereoAng, Yaxis ); VLIMatrix tmatm = VLIMatrix::Translate( -(double)VolNx/2., -(double)VolNy/2., -(double)VolNz/2. ); VLIMatrix mat = VLIMatrix::Identity(); mat *= tmatp; mat *= ymat; mat *= xmat; mat *= tmatm; status = VolContext->GetCamera().SetViewMatrix( mat ); if( status != kVLIOK ) { fprintf( stderr, "SetViewMatrix(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } /* rotate the light backwards: */ State.XangLight -= ( ANGFACT*dy ); State.YangLight -= ( ANGFACT*dx ); c = cos( M_PI*State.YangLight/180. ); s = sin( M_PI*State.YangLight/180. ); x1 = c * LIGHTVX + s * LIGHTVZ; y1 = LIGHTVY; z1 = -s * LIGHTVX + c * LIGHTVZ; c = cos( M_PI*State.XangLight/180. ); s = sin( M_PI*State.XangLight/180. ); vector.X() = x1; vector.Y() = c * y1 - s * z1; vector.Z() = s * y1 + c * z1; status = VolLight->SetDirection( vector ); if( status != kVLIOK ) { fprintf( stderr, "SetDirection(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } else { State.Scale += SCLFACT * (float) ( dx - dy ); if( State.Scale < MIN_SCALE ) State.Scale = MIN_SCALE; /* do not invert */ } } if( ActiveButton & MIDDLE ) { State.Scale += SCLFACT * (float) ( dx - dy ); if( State.Scale < MIN_SCALE ) State.Scale = MIN_SCALE; /* do not invert */ } Xmouse = x; /* new current position */ Ymouse = y; glutSetWindow( GraphicsWindow ); glutPostRedisplay(); } /** ** see if this point is along the current channel's curve: ** ** if it is already a point, return it ** ** if it is nopt, but on the curve, create it **/ struct scpt * PickPoint( float s, float v ) { struct scpt *p; struct scpt **head; /* address of the head of the */ /* current channel's list */ struct scpt *cp; /* new current point */ float s0, v0, s1, v1; /* line endpoints */ float v01; /* interpolated point */ int i; /* the window is a little larger than it should be, */ /* so do a sanity check onthe values that come in: */ if( s < 0. ) s = 0.; if( s > LutSize - 1 ) s = LutSize - 1; if( v < 0. ) v = 0.; if( v > 1. ) v = 1.; if( Verbose ) fprintf( stderr, "PickPoint: s = %6.2f, v = %8.2f\n", s, v ); /* find start of list: */ switch( CurrentChannel ) { case RED: head = &RedPts; break; case GREEN: head = &GreenPts; break; case BLUE: head = &BluePts; break; case ALPHA: head = &AlphaPts; break; } p = *head; if( p == NULL ) { fprintf( stderr, "WARNING: p == NULL in PickPoint\n" ); } /* see if we have picked close to an existing point */ /* on the current curve: */ for( p = *head, i = 0; p != NULL; p = p->next, i++ ) { if( fabs( s - p->s ) <= STOL && fabs( v - p->v ) <= VTOL ) { return p; } } /* next see if we have picked close to the current curve: */ /* if yes, then create a new point there */ /* assume there are always >= 2 points in the list: */ /* assume first point will always have s == 0. */ for( p = *head; p->next != NULL; p = p->next ) { /* if s matches an existing s, then we cannot create */ /* a new point at an s that is already being used: */ if( s == p->s ) break; if( s == p->next->s ) break; /* if we are not in the current gap, keep looping: */ if( s < p->s || s > p->next->s ) continue; /* we are between p and p->next: */ s0 = p->s; v0 = p->v; s1 = p->next->s; v1 = p->next->v; if( s0 == s1 ) { fprintf( stderr, "WARNING: s0 == s1 in InsertPoint\n" ); return NULL; } v01 = v0 + ( s - s0 ) * ( v1 - v0 ) / ( s1 - s0 ); /* insert a new point if we are close: */ if( fabs( v - v01 ) <= VTOL ) { cp = InsertPoint( s, v ); return cp; } } /* next see if we have picked close to an existing point */ /* not on the current curve: */ /* change the current curve if we have */ if( CurrentChannel != RED ) { for( p = RedPts, i = 0; p != NULL; p = p->next, i++ ) { if( fabs( s - p->s ) <= STOL && fabs( v - p->v ) <= VTOL ) { CurrentChannel = RED; return NULL; } } } if( CurrentChannel != GREEN ) { for( p = GreenPts, i = 0; p != NULL; p = p->next, i++ ) { if( fabs( s - p->s ) <= STOL && fabs( v - p->v ) <= VTOL ) { CurrentChannel = GREEN; return NULL; } } } if( CurrentChannel != BLUE ) { for( p = BluePts, i = 0; p != NULL; p = p->next, i++ ) { if( fabs( s - p->s ) <= STOL && fabs( v - p->v ) <= VTOL ) { CurrentChannel = BLUE; return NULL; } } } if( CurrentChannel != ALPHA ) { for( p = AlphaPts, i = 0; p != NULL; p = p->next, i++ ) { if( fabs( s - p->s ) <= STOL && fabs( v - p->v ) <= VTOL ) { CurrentChannel = ALPHA; return NULL; } } } /* last see if we have picked close to a not current curve: */ /* if yes, then make it the current curve: */ /* see above for comments about the code... */ if( CurrentChannel != RED ) { for( p = RedPts; p->next != NULL; p = p->next ) { if( s == p->s ) break; if( s == p->next->s ) break; if( p->next->s < s ) continue; s0 = p->s; v0 = p->v; s1 = p->next->s; v1 = p->next->v; if( s0 == s1 ) { fprintf( stderr, "WARNING: s0 == s1 in InsertPoint\n" ); return NULL; } v01 = v0 + ( s - s0 ) * ( v1 - v0 ) / ( s1 - s0 ); if( fabs( v - v01 ) <= VTOL ) { CurrentChannel = RED; #ifdef OLDWAY cp = InsertPoint( s, v ); #endif return NULL; } } } if( CurrentChannel != GREEN ) { for( p = GreenPts; p->next != NULL; p = p->next ) { if( s == p->s ) break; if( s == p->next->s ) break; if( p->next->s < s ) continue; s0 = p->s; v0 = p->v; s1 = p->next->s; v1 = p->next->v; if( s0 == s1 ) { fprintf( stderr, "WARNING: s0 == s1 in InsertPoint\n" ); return NULL; } v01 = v0 + ( s - s0 ) * ( v1 - v0 ) / ( s1 - s0 ); if( fabs( v - v01 ) <= VTOL ) { CurrentChannel = GREEN; #ifdef OLDWAY cp = InsertPoint( s, v ); #endif return NULL; } } } if( CurrentChannel != BLUE ) { for( p = BluePts; p->next != NULL; p = p->next ) { if( s == p->s ) break; if( s == p->next->s ) break; if( p->next->s < s ) continue; s0 = p->s; v0 = p->v; s1 = p->next->s; v1 = p->next->v; if( s0 == s1 ) { fprintf( stderr, "WARNING: s0 == s1 in InsertPoint\n" ); return NULL; } v01 = v0 + ( s - s0 ) * ( v1 - v0 ) / ( s1 - s0 ); if( fabs( v - v01 ) <= VTOL ) { CurrentChannel = BLUE; #ifdef OLDWAY cp = InsertPoint( s, v ); #endif return NULL; } } } if( CurrentChannel != ALPHA ) { for( p = AlphaPts; p->next != NULL; p = p->next ) { if( s == p->s ) break; if( s == p->next->s ) break; if( p->next->s < s ) continue; s0 = p->s; v0 = p->v; s1 = p->next->s; v1 = p->next->v; if( s0 == s1 ) { fprintf( stderr, "WARNING: s0 == s1 in InsertPoint\n" ); return NULL; } v01 = v0 + ( s - s0 ) * ( v1 - v0 ) / ( s1 - s0 ); if( fabs( v - v01 ) <= VTOL ) { CurrentChannel = ALPHA; #ifdef OLDWAY cp = InsertPoint( s, v ); #endif return NULL; } } } /* we have tried everything to get a new point, but nothing worked */ /* return a failure indicator: */ return NULL; } /** ** quit the program gracefully: **/ void Quit( void ) { /* gracefully close the graphics window: */ Glui->close(); glutDestroyWindow( LutWindow ); glutDestroyWindow( GraphicsWindow ); /* gracefully exit the program: */ exit( 0 ); } #define TOP 2147483647.f /* 2^31 - 1 */ float Ranf( float low, float high ) /* low and high are the limits within which to generate */ /* the random number */ { float r; /* random number */ #ifdef NOTDEF r = (float) random(); #endif r = TOP/2.; return low + r * ( high - low ) / TOP ; } /** ** read the Lut from a file: **/ void ReadState( void ) { FILE *fp; int nr, ng, nb, na; /* number of points in each curve */ int i; /* counter */ float s, v; /* scalar,value pair */ /* open the file: */ fp = fopen( StateFilename, "r" ); if( fp == NULL ) { fprintf( stderr, "Cannot find LUT input file '%s'\n", StateFilename ); return; } /* how many points in each curve: */ fscanf( fp, "%d %d %d %d", &nr, &ng, &nb, &na ); /* read in the points: */ CurrentChannel = RED; FreeList(); for( i = 0; i < nr; i++ ) { fscanf( fp, "%f %f", &s, &v ); InsertPoint( s, v ); } CurrentChannel = GREEN; FreeList(); for( i = 0; i < ng; i++ ) { fscanf( fp, "%f %f", &s, &v ); InsertPoint( s, v ); } CurrentChannel = BLUE; FreeList(); for( i = 0; i < nb; i++ ) { fscanf( fp, "%f %f", &s, &v ); InsertPoint( s, v ); } CurrentChannel = ALPHA; FreeList(); for( i = 0; i < na; i++ ) { fscanf( fp, "%f %f", &s, &v ); InsertPoint( s, v ); } fclose( fp ); SetAlphaRange( CUSTOM ); SetColorRange( CUSTOM ); } /** ** read a volume file: **/ int ReadVolume( struct volume *v ) { VLIuint32 format; /* lut format */ FILE *fp; char *buf; /* buffer to hold magic # */ int len; /* size of buf */ int type; /* VOX file or VOLS file */ if( Verbose ) fprintf( stderr, "ReadVolume()\n" ); /* determine what type of file this is: */ len = strlen( VOXMAGICNUMBER ); if( (int)strlen(VOLSMAGICNUMBER) > len ) len = strlen(VOLSMAGICNUMBER); buf = new char[len+1]; fp = fopen( v->VolumeFilename, "rb" ); if( fp == NULL ) { fprintf( stderr, "ReadVolume(): Cannot open file '%s'\n", v->VolumeFilename ); return DOESNTEXIST; } fread( buf, 1, len, fp ); buf[len] = '\0'; fclose( fp ); if( strncmp( buf, VOXMAGICNUMBER, strlen(VOXMAGICNUMBER) ) == 0 ) type = VOXFILE; else if( strncmp( buf, VOLSMAGICNUMBER, strlen(VOLSMAGICNUMBER) ) == 0 ) type = VOLSFILE; else { fprintf( stderr, "Cannot determine type of file '%s'\n", v->VolumeFilename ); fprintf( stderr, "Magic number = '%s'\n", buf ); delete [] buf; return BADTYPE; } delete [] buf; if( type == VOXFILE ) { v->VolData = VLIVolume::CreateFromFile( v->VolumeFilename ); v->VolData->GetSize( v->VolNx, v->VolNy, v->VolNz ); } if( type == VOLSFILE ) { fprintf( stderr, "Cannot yet handle VOLS file '%s'\n", v->VolumeFilename ); return BADTYPE; } if( Verbose ) fprintf( stderr, "'%s' Volume size = %d x %d x %d\n", v->VolumeFilename, v->VolNx, v->VolNy, v->VolNz ); /**?? do this for now, hopefully they are all the same size: ??**/ VolNx = v->VolNx; VolNy = v->VolNy; VolNz = v->VolNz; /**?? do this for now, hopefully they are all the same format: ??**/ format = v->VolData->GetFormat(); switch( format ) { case kVLIVoxelFormatUINT8: LutSize = 256; break; case kVLIVoxelFormatUINT12L: case kVLIVoxelFormatUINT12U: LutSize = 4096; break; default: fprintf( stderr, "Unknown volume format: %d\n", format ); } return OK; } /** ** reset the transformations and the colors: ** ** this only sets the global variables -- ** the main loop is responsible for redrawing the scene **/ void Reset( void ) { ActiveButton = 0; AnimFrames = 25; AnimT = 0.; AnimDt = 1. / (float)AnimFrames; State.AxesOn = OFF; State.Band = BANDPASS; State.BlendRegion = ORIGBLENDREGION; State.BoundingBoxOn = TRUE; State.DiffuseSpecular = 0.25; State.EmissionLightModel = 0.50; State.MaxAlpha = ORIGMAXALPHA; State.Negative = FALSE; State.NowVolume = 0; State.Projection = ORTHO; State.Roll = 0; State.Scale = 1.0; State.Scale2 = 0.0; /* because add 1. to it in Display() */ State.StereoView = STEREOMIDDLE; State.VolMin = 1; State.VolMax = LutSize - 1; State.Xang = State.Yang = 0.; State.XangCut = State.YangCut = 0.; State.XangLight = State.YangLight = 0.; TransformMode = ROTATE; CurrentChannel = ALPHA; TransXYZ[0] = TransXYZ[1] = TransXYZ[2] = 0.; RotMatrix[0][1] = RotMatrix[0][2] = RotMatrix[0][3] = 0.; RotMatrix[1][0] = RotMatrix[1][2] = RotMatrix[1][3] = 0.; RotMatrix[2][0] = RotMatrix[2][1] = RotMatrix[2][3] = 0.; RotMatrix[3][0] = RotMatrix[3][1] = RotMatrix[3][3] = 0.; RotMatrix[0][0] = RotMatrix[1][1] = RotMatrix[2][2] = RotMatrix[3][3] = 1.; State0 = State; State1 = State; /* prime for animation: */ State0.NowVolume = 0; State1.NowVolume = NumVolumes - 1; } /** ** called when user resizes the window: **/ void Resize ( int width, /* new value for window width */ int height /* new value for window height */ ) { glutSetWindow( GraphicsWindow ); glutPostRedisplay(); } /** ** do the actual file saving: **/ void SaveFile( int id ) { int ret; fprintf( stderr, "Saving '%s'...\n", (char *)SaveFilename ); switch( SaveFormat ) { case SAVEPPM: ret = WritePpm( (char *)SaveFilename ); break; case SAVEVOLC: ret = WriteVolc( (char *)SaveFilename ); break; case SAVESTATE: ret = WriteState( (char *)SaveFilename ); break; } CloseSave( 0 ); } /** ** trigger the Save As window: **/ void SaveFileDialog( int id ) { GLUI_Listbox *formatList; GLUI_EditText *saveFile; GLUI_Panel *panel; GLUI_Button *button; SaveWindow = GLUI_Master.create_glui( "Save File" ); SaveWindow->set_main_gfx_window( GraphicsWindow ); formatList = SaveWindow->add_listbox( "Format", &SaveFormat ); { formatList->set_alignment( GLUI_ALIGN_RIGHT ); formatList->add_item( SAVESTATE, "vx state" ); formatList->add_item( SAVEVOLC, "Volc volume" ); formatList->add_item( SAVEPPM, "PPM screendump" ); formatList->set_int_val( SAVESTATE ); } saveFile = SaveWindow->add_edittext( "File Name", GLUI_EDITTEXT_TEXT, SaveFilename, 0 ); saveFile->set_w( 200 ); saveFile->set_alignment( GLUI_ALIGN_RIGHT ); panel = SaveWindow->add_panel( "", GLUI_PANEL_NONE ); button = SaveWindow->add_button_to_panel( panel, "Save", 0, (GLUI_Update_CB) SaveFile ); button->set_alignment( GLUI_ALIGN_LEFT ); SaveWindow->add_column_to_panel( panel, 0 ); button = SaveWindow->add_button_to_panel( panel, "Cancel", 0, (GLUI_Update_CB) CloseSave ); button->set_alignment( GLUI_ALIGN_RIGHT ); } /** ** return the time in floating point seconds **/ double Seconds( void ) { double d; #ifndef WIN32 struct timeval tv; gettimeofday( &tv, 0 ); d = tv.tv_sec + 0.000001 * tv.tv_usec; #endif #ifdef WIN32 struct tm epoch_s = { 0 }; time_t epoch_t; epoch_s.tm_year = 70; epoch_s.tm_mon = 1; epoch_s.tm_mday = 1; epoch_t = mktime( &epoch_s ); if( epoch_t == (time_t)( -1 ) ) return 0.; /*error*/ else d = difftime( time(NULL), epoch_t ); #endif return d; } /** ** set a particular color range into the sculpted curves: **/ void SetAlphaRange( int type ) { int last; /* last element of lut */ int e; /* lut entry */ struct scpt *p; /* sculpted point list entry */ float v; /* opacity value */ int tempc; /* save current channel */ if( Verbose ) fprintf( stderr, "SetAlphaRange()\n" ); /* if we are here because of a range slider, set the opacity */ /* curve based on VolMin, VolMax, and the size of */ /* the blending region: */ /* (the other reason we could be here is because of */ /* sculpting a point) */ if( type == SLIDER ) { /* free alpha linked lists: */ tempc = CurrentChannel; CurrentChannel = ALPHA; FreeList(); /* get the last element in the lut: */ last = LutSize - 1; /* start: */ /* note: for band pass, Band = PASS */ /* for band reject, Band = REJECT */ v = ( State.Band == BANDPASS ) ? 0. : State.MaxAlpha; if( State.VolMin > 0 ) InsertPoint( 0., v ); /* stay level at the bottom: */ if( State.VolMin - State.BlendRegion > 0 ) InsertPoint( State.VolMin - State.BlendRegion, v ); /* blend to top: */ v = ( State.Band == BANDPASS ) ? State.MaxAlpha : 0.; InsertPoint( State.VolMin, v ); /* plateau at top: */ InsertPoint( State.VolMax, v ); /* blend to bottom: */ v = ( State.Band == BANDPASS ) ? 0. : State.MaxAlpha; if( State.VolMax + State.BlendRegion < last ) InsertPoint( State.VolMax + State.BlendRegion, v ); /* stay level at the bottom: */ if( State.VolMax < last ) InsertPoint( last, v ); CurrentChannel = tempc; } /* interpolate the alpha points into the LUT: */ for( p = AlphaPts; p->next != NULL; p = p->next ) { for( e = p->s; e <= p->next->s; e++ ) { v = p->v + ( e - p->s ) * ( p->next->v - p->v ) / ( p->next->s - p->s ); VolLUT->SetAlphaEntry( (int)e, v ); } } } /** ** set the VP 500 CLUT from the Lut*[] arrays: **/ void SetCLT( void ) { int i; /* vp500 lut entry */ int ii; /* Lut*[] entry */ if( Verbose ) fprintf( stderr, "SetCLT(): %d, %d\n", LutSize, State.Roll ); for( i = 0; i < LutSize; i++ ) { ii = i + State.Roll; if( ii >= LutSize ) ii -= LutSize; if( ! State.Negative ) VolLUT->SetColorEntry( i, State.LutRed[ii], State.LutGreen[ii], State.LutBlue[ii] ); else VolLUT->SetColorEntry( i, 1. - State.LutRed[ii], 1. - State.LutGreen[ii], 1. - State.LutBlue[ii] ); } /* redraw the histogram window: */ CreateHistogram(); } /** ** set a particular color range into the sculpted curves: **/ void SetColorRange( int type ) { float last; /* last element of lut */ int e; /* lut entry */ struct scpt *p; /* sculpted point list entry */ if( Verbose ) fprintf( stderr, "SetColorRange()\n" ); if( type != CUSTOM ) { /* free all color linked lists: */ FreeAllColorLists(); /* get the size of the lut: */ last = (float)( LutSize - 1 ); } /* do different things depending on what color range we want: */ switch( type ) { case CUSTOM: break; case GRAYSCALE: InsertColor( 0., 0., 0., 0. ); InsertColor( last, 1., 1., 1. ); break; case RAINBOW: InsertColor( 0., 0., 0., 1. ); InsertColor( last/4., 0., 1., 1. ); InsertColor( last/2., 0., 1., 0. ); InsertColor( 3.*last/4., 1., 1., 0. ); InsertColor( last, 1., 0., 0. ); break; case HEATEDOBJ: InsertColor( 0., 0., 0., 0. ); InsertColor( last/3., 1., 0., 0. ); InsertColor( 2.*last/3., 1., 1., 0. ); InsertColor( last, 1., 1., 1. ); break; case BROWNGREEN: InsertColor( 0., .3f, .3f, 0. ); InsertColor( last/2., .3f, 1., 0. ); InsertColor( last, 0., 1., 0. ); break; } /* interpolate these points into the LUT: */ for( p = RedPts; p->next != NULL; p = p->next ) { for( e = p->s; e <= p->next->s; e++ ) { State.LutRed[ (int)e ] = p->v + ( e - p->s ) * ( p->next->v - p->v ) / ( p->next->s - p->s ); } } for( p = GreenPts; p->next != NULL; p = p->next ) { for( e = p->s; e <= p->next->s; e++ ) { State.LutGreen[ (int)e ] = p->v + ( e - p->s ) * ( p->next->v - p->v ) / ( p->next->s - p->s ); } } for( p = BluePts; p->next != NULL; p = p->next ) { for( e = p->s; e <= p->next->s; e++ ) { State.LutBlue[ (int)e ] = p->v + ( e - p->s ) * ( p->next->v - p->v ) / ( p->next->s - p->s ); } } /* write into the VP500 LUT: */ SetCLT(); } /** ** set the light behavior based on the EmissionLightModel and DiffuseSpecular global variables ** ** EmissionLightModel: ** 0.0 = emission only ** 1.0 = light model only ** ** DiffuseSpecular: ** 0.0 = diffuse only ** 1.0 = specular only **/ void SetLightModel( void ) { float e, lm; /* how much emission, how much light model */ float d, s; /* how much diffuse, how much specular */ char str[128]; /* label string */ VLIStatus status; if( State.EmissionLightModel <= 0.50 ) { e = 1.; lm = 2.*State.EmissionLightModel; } else { e = 2. - 2.*State.EmissionLightModel; lm = 1.; } if( State.DiffuseSpecular <= 0.50 ) { d = 1.; s = 2.*State.DiffuseSpecular; } else { d = 2. - 2.*State.DiffuseSpecular; s = 1.; } status = VolContext->SetReflectionProperties( lm*d, lm*s, e, SPECULAR_EXPONENT ); if( status != kVLIOK ) { fprintf( stderr, "SetReflectionProperties(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } sprintf( str, EMISSIONFORMAT, (int)(100.*e), (int)(100.*lm) ); EmmissionLabel->set_text( str ); sprintf( str, DIFFUSEFORMAT, (int)(100.*d), (int)(100.*s) ); DiffuseLabel->set_text( str ); glutSetWindow( GraphicsWindow ); glutPostRedisplay(); } /** ** slider callback: **/ void Sliders( int id ) { VLIStatus status; char str[256]; switch( id ) { case VOL: sprintf( str, VOLFORMAT, State.VolMin, State.VolMax ); VolLabel->set_text( str ); SetAlphaRange( SLIDER ); glutSetWindow( LutWindow ); glutPostRedisplay(); break; case MAXALPHA: sprintf( str, MAXALPHAFORMAT, State.MaxAlpha ); MaxAlphaLabel->set_text( str ); SetAlphaRange( SLIDER ); glutSetWindow( LutWindow ); glutPostRedisplay(); break; case BLENDREGION: sprintf( str, BLENDREGIONFORMAT, State.BlendRegion ); BlendRegionLabel->set_text( str ); SetAlphaRange( SLIDER ); glutSetWindow( LutWindow ); glutPostRedisplay(); break; case VOLXCROP: sprintf( str, VOLXCROPFORMAT, State.VolXCropMin, State.VolXCropMax ); VolXCropLabel->set_text( str ); status = VolCrop.SetXSlab( State.VolXCropMin, State.VolXCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetXSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->SetCrop( VolCrop ); if( status != kVLIOK ) { fprintf( stderr, "SetCrop(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } break; case VOLYCROP: sprintf( str, VOLYCROPFORMAT, State.VolYCropMin, State.VolYCropMax ); VolYCropLabel->set_text( str ); status = VolCrop.SetYSlab( State.VolYCropMin, State.VolYCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetXSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->SetCrop( VolCrop ); if( status != kVLIOK ) { fprintf( stderr, "SetCrop(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } break; case VOLZCROP: sprintf( str, VOLZCROPFORMAT, State.VolZCropMin, State.VolZCropMax ); VolZCropLabel->set_text( str ); status = VolCrop.SetZSlab( State.VolZCropMin, State.VolZCropMax ); if( status != kVLIOK ) { fprintf( stderr, "SetXSlab(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } status = VolContext->SetCrop( VolCrop ); if( status != kVLIOK ) { fprintf( stderr, "SetCrop(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } break; case CUTPLANE: status = VolCutPlane->SetPlane( State.CutA, State.CutB, State.CutC, State.CutD ); if( status != kVLIOK ) { fprintf( stderr, "SetPlane(): %s\n", VLIUtGetErrorString( status ) ); } sprintf( str, CUTPLANEFORMAT, -State.CutD ); CutPlaneLabel->set_text( str ); status = VolContext->RemoveCutPlane( VolCutPlane ); if( State.CutPlaneOn ) { status = VolContext->AddCutPlane( VolCutPlane ); if( status != kVLIOK ) { fprintf( stderr, "AddCutPlane(): %s\n", VLIUtGetErrorString( status ) ); Quit(); } } break; case ROLL: sprintf( str, ROLLFORMAT, State.Roll ); RollLabel->set_text( str ); SetCLT(); CreateHistogram(); break; case NOWVOLUME: VolFileList->set_int_val( State.NowVolume ); sprintf( str, VOLNUMBERFORMAT, State.NowVolume ); VolNumberLabel->set_text( str ); break; case ANIMFRAMES: sprintf( str, ANIMFRAMESFORMAT, AnimFrames ); AnimFramesLabel->set_text( str ); break; case ANIMT: sprintf( str, ANIMTFORMAT, (int)( AnimFrames*AnimT) ); AnimTLabel->set_text( str ); break; } glutSetWindow( GraphicsWindow ); glutPostRedisplay(); } /** ** swap the bytes from PC <-> UNIX: **/ void SwapBytes( unsigned char *c4 ) { unsigned char tmp0, tmp1; tmp0 = c4[0]; tmp1 = c4[1]; c4[0] = c4[3]; c4[1] = c4[2]; c4[2] = tmp1; c4[3] = tmp0; } /** ** handle a change to the window's visibility: **/ void Visibility ( int state /* GLUT_VISIBLE or GLUT_NOT_VISIBLE */ ) { if( state == GLUT_VISIBLE ) { glutSetWindow( GraphicsWindow ); glutPostRedisplay(); } else { /* could optimize by keeping track of the fact */ /* that the window is not visible and avoid */ /* redrawing it later ... */ } } /** ** Perform an OpenGL screen dump and write a PPM image file: ** If successful, return 0 **/ int WritePpm( char *filename ) { FILE *fp; /* pointer to PPM file */ unsigned char *array; /* array to hold the RGB dump */ unsigned char *p; /* pointer into array[] */ int dx, dy; /* window size in pixels */ int i, j; /* counters */ int ipix; /* hex pixel count */ /* try to open the file: */ fp = fopen( filename, "w" ); if( fp == NULL ) { fprintf( stderr, "Cannot open PPM file '%s'\n", filename ); return 1; } /* allocate the array to hold the RGB pixel values: */ glutSetWindow( GraphicsWindow ); dx = glutGet( GLUT_WINDOW_WIDTH ); dy = glutGet( GLUT_WINDOW_HEIGHT ); array = new unsigned char[3*dx*dy]; if( array == NULL ) { fprintf( stderr, "ScreenDump() cannot malloc the temporary array!\n" ); fclose( fp ); unlink( filename ); /* remove the file we just created */ return 1; } /* setup and do the dump: */ glPixelStorei( GL_PACK_ALIGNMENT, 1 ); glPixelStorei( GL_PACK_ROW_LENGTH, dx ); glReadBuffer( GL_FRONT ); glReadPixels( 0, 0, dx, dy, GL_RGB, GL_UNSIGNED_BYTE, array ); /* write the PPM header: */ fprintf( fp, "P3\n%d %d\n255\n", dx, dy ); /* write the pixels: */ for( i = 0; i < dy; i++ ) { fprintf( fp, "\n\n" ); p = &array[ 3*dx*(dy-1-i) ]; for( j = 0, ipix = 0; j < dx; j++, p += 3 ) { fprintf( fp, "%03d %03d %03d ", (int)*(p+0), (int)*(p+1), (int)*(p+2) ); if( ++ipix >= 5 ) { putc( '\n', fp ); ipix = 0; } } } /* end game: */ fflush( fp ); fclose( fp ); delete [] array; return 0; } /** ** write the State to a file: **/ int WriteState( char *filename ) { FILE *fp; struct scpt *p; /* loop through linked list */ int nr, ng, nb, na; /* point counts */ /* try to open the file: */ fp = fopen( filename, "w" ); if( fp == NULL ) { fprintf( stderr, "Cannot open State output file '%s'\n", filename ); return 1; } /* count the sculpted points and print them: */ for( p = RedPts, nr = 0; p != NULL; p = p->next ) nr++; for( p = GreenPts, ng = 0; p != NULL; p = p->next ) ng++; for( p = BluePts, nb = 0; p != NULL; p = p->next ) nb++; for( p = AlphaPts, na = 0; p != NULL; p = p->next ) na++; fprintf( fp, "%3d %3d %3d %3d\n", nr, ng, nb, na ); /* print the s,v pairs for red, green, blue, and alpha: */ fprintf( fp, "\n\n\n" ); for( p = RedPts; p != NULL; p = p->next ) fprintf( fp, "%6.1f\t\t%6.3f\n", p->s, p->v ); fprintf( fp, "\n\n\n" ); for( p = GreenPts; p != NULL; p = p->next ) fprintf( fp, "%6.1f\t\t%6.3f\n", p->s, p->v ); fprintf( fp, "\n\n\n" ); for( p = BluePts; p != NULL; p = p->next ) fprintf( fp, "%6.1f\t\t%6.3f\n", p->s, p->v ); fprintf( fp, "\n\n\n" ); for( p = AlphaPts; p != NULL; p = p->next ) fprintf( fp, "%6.1f\t\t%6.3f\n", p->s, p->v ); fclose( fp ); if( Verbose ) fprintf( stderr, "File '%s' written.\n", StateFilename ); return 0; } /** ** write a .volc volume file with the current alt and clt: ** if successful, return 0 **/ int WriteVolc( char *filename ) { int i; int ii; /* Lut*[] entry */ FILE *fp; /* output file pointer */ unsigned int sx, sy; unsigned char *array8; /* mapped volume */ unsigned short *array12; /* mapped volume */ int ix, iy, iz; /* array indices */ int index; /* index into the linear volume */ float fa; /* floating point alpha (0.-1.) */ unsigned int r, g, b, a; /* rgba values */ int int32; /* 32-bit integer to pack for volc */ /* try to create this filename: */ fp = fopen( filename, "wb" ); if( fp == NULL ) { fprintf( stderr, "Cannot create volc file '%s'\n", filename ); return 1; } /* write the header: */ fwrite( "#VOLC\n", 6, 1, fp ); /* dump the volume dimensions: */ int32 = VolNx; #ifdef WIN32 SwapBytes( (unsigned char *) &int32 ); #endif fwrite( &int32, 4, 1, fp ); int32 = VolNy; #ifdef WIN32 SwapBytes( (unsigned char *) &int32 ); #endif fwrite( &int32, 4, 1, fp ); int32 = VolNz; #ifdef WIN32 SwapBytes( (unsigned char *) &int32 ); #endif fwrite( &int32, 4, 1, fp ); /* map the volume to an array and write each voxel: */ if( LutSize == 256 ) { Volumes[ State.NowVolume ].VolData->MapVolume( kVLIAccessReadExclusive, (void *&)array8, sx, sy ); for( ix = 0; ix < (int)VolNx; ix++ ) { for( iy = 0; iy < (int)VolNy; iy++ ) { for( iz = 0; iz < (int)VolNz; iz++ ) { index = iz*VolNx*VolNy + iy*VolNx + ix; i = array8[ index ]; ii = i + State.Roll; if( ii >= LutSize ) ii -= LutSize; if( ! State.Negative ) { r = ( unsigned int ) ( 1023. * State.LutRed[ii] ); g = ( unsigned int ) ( 4095. * State.LutGreen[ii] ); b = ( unsigned int ) ( 1023. * State.LutBlue[ii] ); } else { r = ( unsigned int ) ( 1023. * ( 1. - State.LutRed[ii] ) ); g = ( unsigned int ) ( 4095. * ( 1. - State.LutGreen[ii] ) ); b = ( unsigned int ) ( 1023. * ( 1. - State.LutBlue[ii] ) ); } r &= 0x3ff; g &= 0xfff; b &= 0x3ff; int32 = ( r << 22 ) | ( g << 10 ) | ( b << 0 ); #ifdef WIN32 SwapBytes( (unsigned char *) &int32 ); #endif fwrite( &int32, 4, 1, fp ); VolLUT->GetAlphaEntries( ii, 1, &fa ); a = ( unsigned int ) ( 65536. * fa ); a &= 0xffff; int32 = ( a << 16 ) | ( a << 0 ); #ifdef WIN32 SwapBytes( (unsigned char *) &int32 ); #endif fwrite( &int32, 4, 1, fp ); } } } } else { Volumes[ State.NowVolume ].VolData->MapVolume( kVLIAccessReadExclusive, (void *&)array12, sx, sy ); for( ix = 0; ix < (int)VolNx; ix++ ) { for( iy = 0; iy < (int)VolNy; iy++ ) { for( iz = 0; iz < (int)VolNz; iz++ ) { index = iz*VolNx*VolNy + iy*VolNx + ix; i = array12[ index ]; ii = i + State.Roll; if( ii >= LutSize ) ii -= LutSize; if( ! State.Negative ) { r = ( unsigned int ) ( 1023. * State.LutRed[ii] ); g = ( unsigned int ) ( 4095. * State.LutGreen[ii] ); b = ( unsigned int ) ( 1023. * State.LutBlue[ii] ); } else { r = ( unsigned int ) ( 1023. * ( 1. - State.LutRed[ii] ) ); g = ( unsigned int ) ( 4095. * ( 1. - State.LutGreen[ii] ) ); b = ( unsigned int ) ( 1023. * ( 1. - State.LutBlue[ii] ) ); } r &= 0x3ff; g &= 0xfff; b &= 0x3ff; int32 = ( r << 22 ) | ( g << 10 ) | ( b << 0 ); #ifdef WIN32 SwapBytes( (unsigned char *) &int32 ); #endif fwrite( &int32, 4, 1, fp ); VolLUT->GetAlphaEntries( ii, 1, &fa ); a = ( unsigned int ) ( 65536. * fa ); a &= 0xffff; int32 = ( a << 16 ) | ( a << 0 ); #ifdef WIN32 SwapBytes( (unsigned char *) &int32 ); #endif fwrite( &int32, 4, 1, fp ); } } } } /* unmap the volume: */ Volumes[ State.NowVolume ].VolData->UnmapVolume(); /* done: */ fclose( fp ); return 0; } /* the stroke characters 'X' 'Y' 'Z' : */ static float xx[] = { 0., 1., 0., 1. }; static float xy[] = { -.5, .5, .5, -.5 }; static int xorder[] = { 1, 2, -3, 4 }; static float yx[] = { 0., 0., -.5, .5 }; static float yy[] = { 0., .6f, 1., 1. }; static int yorder[] = { 1, 2, 3, -2, 4 }; static float zx[] = { 1., 0., 1., 0., .25, .75 }; static float zy[] = { .5, .5, -.5, -.5, 0., 0. }; static int zorder[] = { 1, 2, 3, 4, -5, 6 }; /* fraction of the length to use as height of the characters: */ #define LENFRAC 0.10 /* fraction of length to use as start location of the characters: */ #define BASEFRAC 1.10 /* how much the axis sticks out of the bounding box: */ #define EXTRA 1.05 /** ** Draw a set of 3D axes: ** (length is the axis length in world coordinates) **/ void VolAxes( void ) { int i, j; /* counters */ float fact; /* character scale factor */ float base; /* character start location */ float lx, ly, lz; /* lengths */ float length; /* maximum volumelength */ lx = EXTRA * (float)VolNx; ly = EXTRA * (float)VolNy; lz = EXTRA * (float)VolNz; glBegin( GL_LINES ); glVertex3f( 0., 0., 0. ); glVertex3f( lx, 0., 0. ); glVertex3f( 0., 0., 0. ); glVertex3f( 0., ly, 0. ); glVertex3f( 0., 0., 0. ); glVertex3f( 0., 0., lz ); glEnd(); length = lx > ly ? lx : ly; length = lz > length ? lz : length; fact = LENFRAC * length; base = BASEFRAC * lx; glBegin( GL_LINE_STRIP ); for( i = 0; i < 4; i++ ) { j = xorder[i]; if( j < 0 ) { glEnd(); glBegin( GL_LINE_STRIP ); j = -j; } j--; glVertex3f( base + fact*xx[j], fact*xy[j], 0.0 ); } glEnd(); base = BASEFRAC * ly; glBegin( GL_LINE_STRIP ); for( i = 0; i < 5; i++ ) { j = yorder[i]; if( j < 0 ) { glEnd(); glBegin( GL_LINE_STRIP ); j = -j; } j--; glVertex3f( fact*yx[j], base + fact*yy[j], 0.0 ); } glEnd(); base = BASEFRAC * lz; glBegin( GL_LINE_STRIP ); for( i = 0; i < 6; i++ ) { j = zorder[i]; if( j < 0 ) { glEnd(); glBegin( GL_LINE_STRIP ); j = -j; } j--; glVertex3f( 0.0, fact*zy[j], base + fact*zx[j] ); } glEnd(); }