#include "aren.h"

/*****************************************************************************
  This software is copyrighted and owned by the Medical College of Wisconsin.
  See the file README.Copyright for details.
******************************************************************************/

/*============================================================================
  AREN = AFNI Renderer, a set of routines for volume rendering 3D bricks.
  Built on top of VolPack, by Philippe Lacroute.

  (1) Use new_AREN_renderer() to create a renderer.
  (2) Load grayscale or color bricks into it with functions
      AREN_set_graybrick() or AREN_set_rgbbricks().
  (3) Load the opacity brick with AREN_set_opabrick().
  (4) Set the viewing angles with AREN_set_viewpoint().
      Set the brick dimensions with AREN_set_size().
  (5) Create an image with AREN_render().
      Loop back through (2-4) as needed to create new images.
  (6) When finished, use destroy_AREN_renderer() to clean up.
==============================================================================*/

/*--------------------------------------------------------------------------
  Initialize the short -> float color table, and
             the byte  -> float gray/opacity table for VolPack.
  This routine only does something the first time it is called -- it
  creates space for the color tables and fills them.  They will never
  be deallocated.

  N.B.: The documentation for VolPack is incorrect -- the color values
        should be in the range 0..255, NOT 0..1.  The opacity values
        should still be in the range 0..1.
----------------------------------------------------------------------------*/

static float * AREN_colortable = NULL ;
static float * AREN_graytable  = NULL ;
static float * AREN_opatable   = NULL ;

void init_AREN_colortable( void )
{
   static int been_here = 0 ;
   int ii , rr,gg,bb , ss ;

   if( been_here ) return ;  /* done that */

   AREN_colortable = (float *) malloc( sizeof(float) * TOT_COLORS * 3 ) ;
   AREN_graytable  = (float *) malloc( sizeof(float) * MAX_GRAYS ) ;
   AREN_opatable   = (float *) malloc( sizeof(float) * MAX_GRAYS ) ;

   /*-- load linear ramp for grayscale and opacity --*/

   for( ii=0 ; ii < MAX_GRAYS ; ii++ ){
      AREN_graytable[ii] = ii ;
      AREN_opatable[ii]  = ii / 255.0 ;
   }

   /*-- load a 32 x 32 x 32 color cube --*/

   for( rr=0 ; rr < MAX_CDIM ; rr++ ){
      for( gg=0 ; gg < MAX_CDIM ; gg++ ){
         for( bb=0 ; bb < MAX_CDIM ; bb++ ){

            ss = FIVE_to_short(rr,gg,bb) ;   /* color index */

            AREN_colortable[3*ss  ] = (rr * 255.0) / 31.0 ;
            AREN_colortable[3*ss+1] = (gg * 255.0) / 31.0 ;
            AREN_colortable[3*ss+2] = (bb * 255.0) / 31.0 ;
         }
      }
   }

   /*-- at the end, add the pure grays (at a higher resolution) --*/

   ss = 3 * MAX_COLORS ;
   for( ii=0 ; ii < MAX_GRAYS ; ii++ ){
      AREN_colortable[ss++] = ii ;
      AREN_colortable[ss++] = ii ;
      AREN_colortable[ss++] = ii ;
   }

   been_here = 1 ;
   return ;
}

/*--------------------------------------------------------------------------
  Create a new AFNI renderer.
  The return pointer is passed to all other AREN routines.
----------------------------------------------------------------------------*/

#define DEFAULT_THETA 130.0
#define DEFAULT_PHI   285.0

void * new_AREN_renderer( void )
{
   AREN_stuff * ar ;

   ar = (AREN_stuff *) malloc( sizeof(AREN_stuff) ) ;
   ar->type = AREN_TYPE ;

   init_AREN_colortable() ;  /* only does something 1st time in */

   /*-- initialize VolPack --*/

   ar->vpc = vpCreateContext() ;

   vpSeti( ar->vpc , VP_CONCAT_MODE , VP_CONCAT_LEFT ) ;

   vpCurrentMatrix( ar->vpc , VP_MODEL ) ;
   vpIdentityMatrix( ar->vpc ) ;

   vpCurrentMatrix( ar->vpc , VP_VIEW ) ;
   vpIdentityMatrix( ar->vpc ) ;
   vpRotate( ar->vpc , VP_Y_AXIS , DEFAULT_THETA ) ;
   vpRotate( ar->vpc , VP_X_AXIS , DEFAULT_PHI   ) ;

#undef USE_CUEING
#ifdef USE_CUEING
   vpSetDepthCueing( ar->vpc , 1.0 , 0.5 ) ;
   vpEnable( ar->vpc , VP_DEPTH_CUE , 1 ) ;
#endif

   vpCurrentMatrix( ar->vpc , VP_PROJECT ) ;
   vpIdentityMatrix( ar->vpc ) ;
   vpWindow( ar->vpc , VP_PARALLEL , -0.5,0.5 , -0.5,0.5 , -0.5,0.5 ) ;

   /*-- initialize the rest of the data --*/

   ar->nx = ar->ny = ar->nz = ar->verbose = ar->newdata = ar->newvox = 0 ;
   ar->sx = ar->sy = ar->sz = 1.0 ;

   ar->theta = DEFAULT_THETA ;
   ar->phi   = DEFAULT_PHI ;
   ar->grim  = ar->rim = ar->gim = ar->bim = ar->opim = NULL ;
   ar->vox   = NULL ;

   return (void *) ar ;
}

/*-----------------------------------------------------------------------------
   Get rid of an AFNI renderer.
-------------------------------------------------------------------------------*/

void destroy_AREN_renderer( void * ah )
{
   AREN_stuff * ar = (AREN_stuff *) ah ;

   if( !ISVALID_AREN(ar) ) return ;

   if( ar->vox != NULL ) free(ar->vox) ;
   vpDestroyContext( ar->vpc ) ;
   free(ar) ;
   return ;
}

/*-----------------------------------------------------------------------------
   For debugging purposes
-------------------------------------------------------------------------------*/

void AREN_be_verbose( void * ah )
{
   AREN_stuff * ar = (AREN_stuff *) ah ;
   if( !ISVALID_AREN(ar) ) return ;
   ar->verbose = 1 ; return ;
}

void AREN_be_quiet( void * ah )
{
   AREN_stuff * ar = (AREN_stuff *) ah ;
   if( !ISVALID_AREN(ar) ) return ;
   ar->verbose = 0 ; return ;
}

/*-----------------------------------------------------------------------------
   Set the grayscale brick in a renderer.
   Returns -1 if an error occurs, otherwise returns 0.
   Note that this brick is NOT free-d by AREN at any point -- that is
   the user's responsibility.
-------------------------------------------------------------------------------*/

int AREN_set_graybrick( void * ah , MRI_IMAGE * grim )
{
   AREN_stuff * ar = (AREN_stuff *) ah ;
   int newvox=0 , nvox,ii ;
   byte    * gar ;
   rgbvox  * gvox ;
   byte      gb ;

   /*-- sanity checks --*/

   if( !ISVALID_AREN(ar) || grim == NULL || grim->kind != MRI_byte ) return -1 ;

   if( grim->nx < 3 || grim->ny < 3 || grim->nz < 3 ){
      fprintf(stderr,"**AREN: illegal dimensions for a gray brick\n") ;
      return -1 ;
   }

   /*-- if had old color bricks, toss them (or their pointers) --*/

   if( ar->rim != NULL ){
      ar->rim = ar->gim = ar->bim = NULL ;
      if( ar->verbose )
         fprintf(stderr,"--AREN: switching from color bricks to gray brick\n") ;
   } else {
      if( ar->verbose )
         fprintf(stderr,"--AREN: input a new gray brick\n") ;
   }

   /*-- if have new dimensions, toss old stuff that doesn't match --*/

   if( ar->nx > 0 &&
       ( ar->nx != grim->nx || ar->ny != grim->ny || ar->nz != grim->nz ) ){

      ar->opim = NULL ;
      if( ar->vox != NULL ){ free(ar->vox) ; ar->vox = NULL ; }

      if( ar->verbose )
         fprintf(stderr,"--AREN: new gray brick changes volume dimensions\n"
                        "        nx:%d->%d  ny:%d->%d  nz:%d->%d\n",
                        ar->nx,grim->nx , ar->ny,grim->ny , ar->nz,grim->nz ) ;
   }

   /*-- set dimensions --*/

   ar->grim = grim ;
   ar->nx   = grim->nx ;
   ar->ny   = grim->ny ;
   ar->nz   = grim->nz ; nvox = ar->nx * ar->ny * ar->nz ;

   /*-- if need be, allocate a voxel array to hold the data --*/

   if( ar->vox == NULL ){
      ar->newvox = newvox = 1 ;
      ar->vox = (rgbvox *) malloc( sizeof(rgbvox) * nvox ) ;
      if( ar->vox == NULL ){
         fprintf(stderr,"**AREN: can't malloc workspace with new gray brick\n") ;
         return -1 ;
      } else if( ar->verbose ){
         fprintf(stderr,"--AREN: allocated new voxel array\n") ;
      }
   }

   /*-- copy grayscale data into voxel array --*/

   gvox = ar->vox ;
   gar  = MRI_BYTE_PTR(grim) ;
   for( ii=0 ; ii < nvox ; ii++ ) gvox[ii].rgb = (unsigned short) gar[ii] ;

   ar->newdata = 1 ;
   return 0 ;
}

/*-----------------------------------------------------------------------------
   Set the opacity brick in a renderer.
   Returns -1 if an error occurs, otherwise returns 0.
   Note that this brick is NOT free-d by AREN at any point -- that is
   the user's responsibility.
-------------------------------------------------------------------------------*/

int AREN_set_opabrick( void * ah , MRI_IMAGE * opim )
{
   AREN_stuff * ar = (AREN_stuff *) ah ;
   int nvox,ii , newvox ;
   byte    * gar ;
   rgbvox  * rvox ;

   /*-- sanity checks --*/

   if( !ISVALID_AREN(ar) || opim == NULL || opim->kind != MRI_byte ) return -1 ;

   if( opim->nx < 3 || opim->ny < 3 || opim->nz < 3 ){
      fprintf(stderr,"**AREN: illegal dimensions for an opacity brick\n") ;
      return -1 ;
   }

   /*-- if have new dimensions, toss old stuff that doesn't match --*/

   if( ar->nx > 0 &&
       ( ar->nx != opim->nx || ar->ny != opim->ny || ar->nz != opim->nz ) ){

      ar->grim = ar->rim = ar->gim = ar->bim = NULL ;
      if( ar->vox != NULL ){ free(ar->vox) ; ar->vox = NULL ; }

      if( ar->verbose )
         fprintf(stderr,"--AREN: new opacity brick changes volume dimensions\n"
                        "        nx:%d->%d  ny:%d->%d  nz:%d->%d\n",
                        ar->nx,opim->nx , ar->ny,opim->ny , ar->nz,opim->nz ) ;
   } else {
      if( ar->verbose )
         fprintf(stderr,"--AREN: new opacity brick\n") ;
   }

   /*-- set dimensions --*/

   ar->opim = opim ;
   ar->nx   = opim->nx ;
   ar->ny   = opim->ny ;
   ar->nz   = opim->nz ; nvox = ar->nx * ar->ny * ar->nz ;

   /*-- if need be, allocate a voxel array to hold the data --*/

   if( ar->vox == NULL ){
      ar->newvox = newvox = 1 ;
      ar->vox = (rgbvox *) malloc( sizeof(rgbvox) * nvox ) ;
      if( ar->vox == NULL ){
         fprintf(stderr,"**AREN: can't malloc workspace with new opacity brick\n") ;
         return -1 ;
      } else if( ar->verbose ){
         fprintf(stderr,"--AREN: allocated new voxel array\n") ;
      }
   }

   /*-- load the opacity into voxel array --*/

   gar  = MRI_BYTE_PTR(ar->opim) ;
   rvox = ar->vox ;
   for( ii=0 ; ii < nvox ; ii++ ) rvox[ii].alpha = (unsigned short) gar[ii] ;

   ar->newdata = 1 ;
   return 0 ;
}

/*-----------------------------------------------------------------------------
   Set the color bricks in a renderer.
   Returns -1 if an error occurs, otherwise returns 0.
   Note that these bricks are NOT free-d by AREN at any point -- that is
   the user's responsibility.
-------------------------------------------------------------------------------*/

int AREN_set_rgbbricks( void * ah , MRI_IMAGE *rim, MRI_IMAGE *gim, MRI_IMAGE *bim )
{
   AREN_stuff * ar = (AREN_stuff *) ah ;
   int newvox=0 , nvox,ii ;
   byte    * rar , * gar , * bar ;
   rgbvox  * rvox ;
   byte      rb,gb,bb ;

   /*-- sanity checks --*/

   if( rim == NULL || rim->kind != MRI_byte ||
       gim == NULL || gim->kind != MRI_byte ||
       bim == NULL || bim->kind != MRI_byte || !ISVALID_AREN(ar) ) return -1 ;

   if( rim->nx < 3 || rim->ny < 3 || rim->nz < 3 ){
      fprintf(stderr,"**AREN: illegal dimensions for a color brick\n") ; return -1 ;
   }

   if( gim->nx != rim->nx || gim->ny != rim->ny || gim->nz != rim->nz ){
      fprintf(stderr,"**AREN: green & red rgb bricks don't match\n") ; return -1 ;
   }

   if( bim->nx != rim->nx || bim->ny != rim->ny || bim->nz != rim->nz ){
      fprintf(stderr,"**AREN: blue & red rgb bricks don't match\n") ; return -1 ;
   }

   /*-- if had an old gray brick, toss it (or at least its pointer) --*/

   if( ar->grim != NULL ){
      ar->grim = NULL ;
      if( ar->verbose )
         fprintf(stderr,"--AREN: switching from gray brick to color bricks\n") ;
   } else {
      if( ar->verbose )
         fprintf(stderr,"--AREN: input new color bricks\n") ;
   }

   /*-- if have new dimensions, toss old stuff that doesn't match --*/

   if( ar->nx > 0 &&
       ( ar->nx != rim->nx || ar->ny != rim->ny || ar->nz != rim->nz ) ){

      ar->opim = NULL ;
      if( ar->vox != NULL ){ free(ar->vox) ; ar->vox = NULL ; }

      if( ar->verbose )
         fprintf(stderr,"--AREN: new color bricks change volume dimensions\n"
                        "        nx:%d->%d  ny:%d->%d  nz:%d->%d\n",
                        ar->nx,rim->nx , ar->ny,rim->ny , ar->nz,rim->nz ) ;
   }

   /*-- set dimensions --*/

   ar->rim = rim ; ar->gim = gim ; ar->bim = bim ;
   ar->nx  = rim->nx ;
   ar->ny  = rim->ny ;
   ar->nz  = rim->nz ; nvox = ar->nx * ar->ny * ar->nz ;

   /*-- if need be, allocate a voxel array to hold the data --*/

   if( ar->vox == NULL ){
      ar->newvox = newvox = 1 ;
      ar->vox = (rgbvox *) malloc( sizeof(rgbvox) * nvox ) ;
      if( ar->vox == NULL ){
         fprintf(stderr,"**AREN: can't malloc workspace with new color bricks\n") ;
         return -1 ;
      } else if( ar->verbose ){
         fprintf(stderr,"--AREN: allocated new voxel array\n") ;
      }
   }

   /*-- copy color data into voxel array --*/

   rvox = ar->vox ;
   rar  = MRI_BYTE_PTR(rim) ;
   gar  = MRI_BYTE_PTR(gim) ;
   bar  = MRI_BYTE_PTR(bim) ;
   for( ii=0 ; ii < nvox ; ii++ ){
      rb = EIGHT_TO_FIVE(rar[ii]) ;
      gb = EIGHT_TO_FIVE(gar[ii]) ;
      bb = EIGHT_TO_FIVE(bar[ii]) ;

      if( rb == gb && rb == bb ){
         rvox[ii].rgb = MAX_COLORS + rar[ii] ;         /* index into grayscale */
      } else {
         rvox[ii].rgb = FIVE_to_short( rb , gb, bb ) ; /* index into color cube */
      }
   }

   ar->newdata = 1 ;
   return 0 ;
}

/*-------------------------------------------------------------------------------
   Set the viewpoint of the user (in polar angles)
---------------------------------------------------------------------------------*/

void AREN_set_viewpoint( void * ah , float theta , float phi )
{
   AREN_stuff * ar = (AREN_stuff *) ah ;

   if( !ISVALID_AREN(ar) ) return ;

   ar->theta = theta ; ar->phi = phi ;

   vpCurrentMatrix( ar->vpc , VP_VIEW ) ;
   vpIdentityMatrix( ar->vpc ) ;
   vpRotate( ar->vpc , VP_Y_AXIS , theta ) ;
   vpRotate( ar->vpc , VP_X_AXIS , phi   ) ;

   if( ar->verbose )
      fprintf(stderr,"--AREN: set theta=%f  phi=%f\n",theta,phi) ;

   return ;
}

/*-------------------------------------------------------------------------------
   Set the scale factors for each axis (default = 1).  The inputs should
   be the size of the brick along each axes (e.g., sx = nx * dx).  This is
   needed because VolPack assumes the input data is in a unit cube, but
   our data may not be so uniform.

   N.B.: This is disabled, since VolPack doesn't seem to work in this regards.
---------------------------------------------------------------------------------*/

void AREN_set_size( void * ah , float sx , float sy , float sz )
{
#if 0
   AREN_stuff * ar = (AREN_stuff *) ah ;
   float mmm ;

   if( !ISVALID_AREN(ar) ) return ;

   sx = fabs(sx) ; if( sx == 0.0 ) sx = 1.0 ;  /* don't allow non-positive sizes */
   sy = fabs(sy) ; if( sy == 0.0 ) sy = 1.0 ;
   sz = fabs(sz) ; if( sz == 0.0 ) sz = 1.0 ;

   mmm = sx ;
   if( mmm < sy ) mmm = sy ;
   if( mmm < sz ) mmm = sz ;  /* mmm = maximum size */

   ar->sx = sx / mmm ;        /* scale factors are <= 1.0 */
   ar->sy = sy / mmm ;
   ar->sz = sz / mmm ;

   vpCurrentMatrix( ar->vpc , VP_MODEL ) ;  /* scale model to world */
   vpIdentityMatrix( ar->vpc ) ;
   vpScale( ar->vpc , sx , sy , sz ) ;

   if( ar->verbose )
      fprintf(stderr,"--AREN: set scale factors = %f %f %f\n",ar->sx,ar->sy,ar->sz) ;
#endif

   return ;
}

#undef USE_CALLBACKS
#ifdef USE_CALLBACKS
/*------------------------------------------------------------------------------
   Routines that can be used for callback shading, vs. lookup table shading.
   Mostly for debugging purposes.
--------------------------------------------------------------------------------*/

static int ncall = 0 ;

void AREN_grayfunc( void * vp , float * g , void * cd )
{
   rgbvox * rv = (rgbvox *) vp ;

   *g = AREN_graytable[ rv->rgb ] ;

#if 0
   ncall++ ;
   if( ncall%1000 == 0 && rv->rgb > 0 )
      fprintf(stderr,"Grayfunc: rgb=%d g=%f\n",(int)(rv->rgb) , *g ) ;
#endif

   return ;
}

void AREN_colorfunc( void * vp , float * r , float * g , float * b , void * cd )
{
   rgbvox * rv = (rgbvox *) vp ;
   *r = AREN_colortable[ 3*(rv->rgb)   ] ;
   *g = AREN_colortable[ 3*(rv->rgb)+1 ] ;
   *b = AREN_colortable[ 3*(rv->rgb)+2 ] ;

#if 0
   ncall++ ;
   if( ncall%1000 == 0 && rv->rgb > 0 )
      fprintf(stderr,"Colorfunc: rgb=%d r=%f g=%f b=%f\n",
                     (int)(rv->rgb) , *r,*g,*b ) ;
#endif

   return ;
}
#endif /* USE_CALLBACKS */

/*-------------------------------------------------------------------------------
   Actually render an image.  Returns NULL if an error occurs.
   Input npix = number of pixels on a side of the image (always will be square).
   If rendering a grayscale brick, returns an image of kind MRI_byte.
   If rendering RGB bricks, returns an image of kind MRI_rgb.
---------------------------------------------------------------------------------*/

MRI_IMAGE * AREN_render( void * ah , int npix )
{
   AREN_stuff * ar = (AREN_stuff *) ah ;
   int isgray , isrgb ;
   MRI_IMAGE * im ;
   byte * imar ;
   vpResult fred ;

   /*-- sanity checks --*/

   if( !ISVALID_AREN(ar) ) return NULL ;

   if( npix < 16 ){
      fprintf(stderr,"**AREN: attempt to render with less than 16 pixels!\n") ;
      return NULL ;
   }

   isgray = (ar->grim != NULL) ;
   isrgb  = (ar->rim  != NULL) ;

   if( isgray && isrgb ){
      fprintf(stderr,"**AREN: attempt to render gray and color simultaneously?\n");
      return NULL ;
   }

   if( (!isgray && !isrgb) || ar->vox == NULL ){
      fprintf(stderr,"**AREN: attempt to render without data being loaded!\n") ;
      return NULL ;
   }

   if( ar->opim == NULL ){
      fprintf(stderr,"**AREN: attempt to render without opacity being loaded!\n") ;
      return NULL ;
   }

   if( ar->nx < 3 || ar->ny < 3 || ar->nz < 3 ){
      fprintf(stderr,"**AREN: attempt to render without initialization!\n") ;
      return NULL ;
   }

   /*-- if have new voxel array, must tell VolPack all about it --*/

   if( ar->newvox ){
      int nvox = ar->nx * ar->ny * ar->nz ;
      rgbvox vv , *rv = &vv ;

      if( ar->verbose )
         fprintf(stderr,"--AREN: setting up new voxel array\n") ;

      /* 3D dimensions */

      fred = vpSetVolumeSize( ar->vpc , ar->nx , ar->ny , ar->nz ) ;
      if( fred != VP_OK ){
         fprintf(stderr,"**AREN: vpSetVolumeSize failed: code=%d\n",(int)fred) ;
         return NULL ;
      }

      /* each voxel has 2 data fields; 1 for shading and 1 for opacity */

      fred = vpSetVoxelSize( ar->vpc , sizeof(rgbvox) , 2 , 1 , 1 ) ;
      if( fred != VP_OK ){
         fprintf(stderr,"**AREN: vpSetVoxelSize failed: code=%d\n",(int)fred) ;
         return NULL ;
      }

      /* gray scale input */

      if( isgray ){

         /* voxel field 0 (rgb) is an index into AREN_graytable */

         fred = vpSetVoxelField( ar->vpc, 0, sizeof(short),
                                 vpFieldOffset(rv,rgb), MAX_GRAYS-1 );
         if( fred != VP_OK ){
            fprintf(stderr,"**AREN: vpSetVoxelField(0) failed: code=%d\n",(int)fred) ;
            return NULL ;
         }

         /* voxel field 1 (alpha) is an index into AREN_opatable */

         fred = vpSetVoxelField( ar->vpc, 1, sizeof(short),
                                 vpFieldOffset(rv,alpha), MAX_GRAYS-1 );
         if( fred != VP_OK ){
            fprintf(stderr,"**AREN: vpSetVoxelField(1) failed: code=%d\n",(int)fred) ;
            return NULL ;
         }

         /* tell VolPack where the voxels are */

         fred = vpSetRawVoxels( ar->vpc , ar->vox ,
                                sizeof(rgbvox)*nvox ,
                                sizeof(rgbvox) ,
                                sizeof(rgbvox)*(ar->nx) ,
                                sizeof(rgbvox)*(ar->nx * ar->ny) ) ;
         if( fred != VP_OK ){
            fprintf(stderr,"**AREN: vpSetRawVoxels failed: code=%d\n",(int)fred) ;
            return NULL ;
         }

#ifndef USE_CALLBACKS

         /* voxel field 0 (rgb) is an index into AREN_graytable */

         fred = vpSetLookupShader( ar->vpc , 1 , 1 , 0 ,
                                   AREN_graytable , sizeof(float)*MAX_GRAYS ,
                                   0 , NULL , 0 ) ;
         if( fred != VP_OK ){
            fprintf(stderr,"**AREN: vpSetLookupShader failed: code=%d\n",(int)fred) ;
            return NULL ;
         }
#else
         fred = vpSetCallback( ar->vpc , VP_GRAY_SHADE_FUNC , AREN_grayfunc ) ;
         if( fred != VP_OK ){
            fprintf(stderr,"**AREN: vpSetCallback(GRAY) failed: code=%d\n",(int)fred) ;
            return NULL ;
         }
#endif

      /* color input */

      } else if( isrgb ){

         /* voxel field 0 (rgb) is an index into AREN_colortable */

         fred = vpSetVoxelField( ar->vpc, 0, sizeof(short),
                                 vpFieldOffset(rv,rgb), TOT_COLORS-1 );
         if( fred != VP_OK ){
            fprintf(stderr,"**AREN: vpSetVoxelField(0) failed: code=%d\n",(int)fred) ;
            return NULL ;
         }

         /* voxel field 1 (alpha) is an index into AREN_opatable */

         fred = vpSetVoxelField( ar->vpc, 1, sizeof(short) ,
                                 vpFieldOffset(rv,alpha), MAX_GRAYS-1 );
         if( fred != VP_OK ){
            fprintf(stderr,"**AREN: vpSetVoxelField(1) failed: code=%d\n",(int)fred) ;
            return NULL ;
         }

         /* tell VolPack where the voxels are */

         fred = vpSetRawVoxels( ar->vpc , ar->vox ,
                                sizeof(rgbvox)*nvox ,
                                sizeof(rgbvox) ,
                                sizeof(rgbvox)*(ar->nx) ,
                                sizeof(rgbvox)*(ar->nx * ar->ny) ) ;
         if( fred != VP_OK ){
            fprintf(stderr,"**AREN: vpSetRawVoxels failed: code=%d\n",(int)fred) ;
            return NULL ;
         }

#ifndef USE_CALLBACKS

         /* voxel field 0 (rgb) is an index into AREN_colortable */

         fred = vpSetLookupShader( ar->vpc , 3 , 1 , 0 ,
                                   AREN_colortable , sizeof(float)*TOT_COLORS*3 ,
                                   0 , NULL , 0 ) ;
         if( fred != VP_OK ){
            fprintf(stderr,"**AREN: vpSetLookupShader failed: code=%d\n",(int)fred) ;
            return NULL ;
         }
#else
         fred = vpSetCallback( ar->vpc , VP_RGB_SHADE_FUNC , AREN_colorfunc ) ;
         if( fred != VP_OK ){
            fprintf(stderr,"**AREN: vpSetCallback(COLOR) failed: code=%d\n",(int)fred) ;
            return NULL ;
         }
#endif
      }

      /* voxel field 1 (alpha) is an index into AREN_opatable */

      fred = vpSetClassifierTable( ar->vpc, 0, 1, AREN_opatable, sizeof(float)*MAX_GRAYS ) ;
      if( fred != VP_OK ){
         fprintf(stderr,"**AREN: vpSetClassifierTable failed: code=%d\n",(int)fred) ;
         return NULL ;
      }

      /* threshold for octree bins: 12 = 5% of opacity range */

      fred = vpMinMaxOctreeThreshold( ar->vpc , 0 , 12 ) ;
      if( fred != VP_OK ){
         fprintf(stderr,"**AREN: vpMinMaxOctreeThreshold failed: code=%d\n",(int)fred) ;
         return NULL ;
      }

      ar->newvox = 0 ;
   }

   /*-- if have new data in the voxel array, must recreate the octree --*/

#if 1
   vpSetd( ar->vpc , VP_MAX_RAY_OPACITY   , 0.95 ) ;
   vpSetd( ar->vpc , VP_MIN_VOXEL_OPACITY , 0.05 ) ;
#endif

   if( ar->newdata ){

#undef USE_CLASSIFIED  /* for using the "classified" volume feature of VolPack */

#ifndef USE_CLASSIFIED
      if( ar->verbose )
         fprintf(stderr,"--AREN: computing octree\n") ;

      /* make octree, down to 4x4x4 voxel bins */

      fred = vpCreateMinMaxOctree( ar->vpc , 0 , 4 ) ;
      if( fred != VP_OK ){
         fprintf(stderr,"**AREN: vpCreateMinMaxOctree failed: code=%d\n",(int)fred) ;
         return NULL ;
      }
#else
      if( ar->verbose )
         fprintf(stderr,"--AREN: computing classified volume\n") ;

      /* classify volume (slower than octree-ifying) */

      fred = vpClassifyVolume( ar->vpc ) ;
      if( fred != VP_OK ){
         fprintf(stderr,"**AREN: vpClassifyVolume failed: code=%d\n",(int)fred) ;
         return NULL ;
      }
#endif

      ar->newdata = 0 ;
   }

   /*-- create the output image --*/

#undef GET_ALPHA  /* for debugging: compute the opacity image */

   if( isgray ){
      im   = mri_new( npix , npix , MRI_byte ) ;
      imar = MRI_BYTE_PTR(im) ;
#ifndef GET_ALPHA
      vpSetImage( ar->vpc , imar , npix,npix,npix , VP_LUMINANCE ) ;
#else
      vpSetImage( ar->vpc , imar , npix,npix,npix , VP_ALPHA ) ;
#endif
   } else if( isrgb ){
#ifndef GET_ALPHA
      im   = mri_new( npix , npix , MRI_rgb ) ;
      imar = MRI_RGB_PTR(im) ;
      vpSetImage( ar->vpc , imar , npix,npix,3*npix , VP_RGB ) ;
#else
      im   = mri_new( npix , npix , MRI_byte ) ;
      imar = MRI_BYTE_PTR(im) ;
      vpSetImage( ar->vpc , imar , npix,npix,npix , VP_ALPHA ) ;
#endif
   }

   if( ar->verbose )
      fprintf(stderr,"--AREN: rendering image\n") ;

#ifdef USE_CLASSIFIED
   fred = vpRenderClassifiedVolume(ar->vpc) ;
   if( fred != VP_OK ){
      fprintf(stderr,"**AREN: vpRenderClassifiedVolume failed: code=%d\n",(int)fred) ;
      mri_free(im) ; return NULL ;
   }
#else
   fred = vpRenderRawVolume(ar->vpc) ;
   if( fred != VP_OK ){
      fprintf(stderr,"**AREN: vpRenderRawVolume failed: code=%d\n",(int)fred) ;
      mri_free(im) ; return NULL ;
   }
#endif

   return im ;
}


syntax highlighted by Code2HTML, v. 0.9.1