/*=========================================================================

  Program:   Visualization Toolkit
  Module:    $RCSfile: vtkVolumeProVG500Mapper.cxx,v $
  Language:  C++
  Date:      $Date: 2003/02/26 13:58:12 $
  Version:   $Revision: 1.38 $

  Copyright (c) 1993-2002 Ken Martin, Will Schroeder, Bill Lorensen 
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even 
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkVolumeProVG500Mapper.h"

#include "vtkCamera.h"
#include "vtkColorTransferFunction.h"
#include "vtkDebugLeaks.h"
#include "vtkGraphicsFactory.h"
#include "vtkImageData.h"
#include "vtkLight.h"
#include "vtkLightCollection.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLVolumeProVG500Mapper.h"
#include "vtkPiecewiseFunction.h"
#include "vtkPointData.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkToolkits.h"
#include "vtkTransform.h"
#include "vtkVolume.h"
#include "vtkVolumeProperty.h"

#include <math.h>

vtkCxxRevisionMacro(vtkVolumeProVG500Mapper, "$Revision: 1.38 $");

//----------------------------------------------------------------------------
// Needed when we don't use the vtkStandardNewMacro.
vtkInstantiatorNewMacro(vtkVolumeProVG500Mapper);
//----------------------------------------------------------------------------

vtkVolumeProVG500Mapper::vtkVolumeProVG500Mapper()
{
  VLIStatus         status;
  VLIConfiguration  *config;

    // Establish a connection with vli
  status = VLIOpen();
  if ( status != kVLIOK )
    {
    vtkDebugMacro( << "VLIOpen failed!" );
    this->Context = NULL;
    this->LookupTable = NULL;

    if ( status == kVLIErrNoHardware )
      {
      this->NoHardware = 1;
      }
    else if ( status == kVLIErrVersion )
      {
      this->WrongVLIVersion = 1;
      }
    return;
    }

  // Gather some useful information
  config = new VLIConfiguration;
  this->NumberOfBoards = config->GetNumberOfBoards();
  this->MajorBoardVersion = config->GetBoardMajorVersion();
  this->MinorBoardVersion = config->GetBoardMinorVersion();
  this->GradientTableSize = config->GetGradientTableLength();
  delete config;

  // Create the context
  this->Context = VLIContext::Create();
  if (!this->Context)
    {
    vtkErrorMacro( << "Context could not be created!" );
    return;
    }

  this->LookupTable = VLILookupTable::Create(VLILookupTable::kSize4096);

  if ( !this->LookupTable )
    {
    vtkErrorMacro( << "Lookup table could not be created!" );
    return;    
    }

  this->Context->SetLookupTable(this->LookupTable);


  this->Cut = VLICutPlane::Create( 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 );
  
  if ( !this->Cut )
    {
    vtkErrorMacro( << "Cut plane could not be created!" );
    return;    
    }
}



vtkVolumeProVG500Mapper::~vtkVolumeProVG500Mapper()
{
  int i;

  // free the lights
  if (this->NumberOfLights > 0)
    {      
    for ( i = 0; i < this->NumberOfLights; i++ )
      {
      this->Context->RemoveLight( this->Lights[i] );
      this->Lights[i]->Release();
      }
    if ( this->Lights )
      {
      delete [] this->Lights;
      }
    }
  
  if (this->Cut)
    {
    this->Cut->Release();
    }

  // Free the lookup table if it was created
  if ( this->LookupTable )
    {
    this->LookupTable->Release();
    }

  // Free the volume if necessary
  if ( this->Volume )
    {
    this->Volume->Release();
    }

  // Free the context if necessary
  if (this->Context)
    {
    this->Context->Release();
    }

  // Terminate connection to the hardware
  VLIClose();
}

vtkVolumeProVG500Mapper *vtkVolumeProVG500Mapper::New()
{
  // First try to create the object from the vtkObjectFactory
  vtkObject* ret = vtkObjectFactory::CreateInstance("vtkVolumeProVG500Mapper");
  if(ret)
    {
    return (vtkVolumeProVG500Mapper*)ret;
    }
  // If the factory was unable to create the object, then create it here.
  const char *temp = vtkGraphicsFactory::GetRenderLibrary();
  
#ifdef VTK_USE_OGLR
  if (!strcmp("OpenGL",temp))
    {
#ifdef VTK_DEBUG_LEAKS
    vtkDebugLeaks::DestructClass("vtkVolumeProVG500Mapper");
#endif
    return vtkOpenGLVolumeProVG500Mapper::New();
    }
#endif
#ifdef _WIN32
  if (!strcmp("Win32OpenGL",temp))
    {
#ifdef VTK_DEBUG_LEAKS
    vtkDebugLeaks::DestructClass("vtkVolumeProVG500Mapper");
#endif
    return vtkOpenGLVolumeProVG500Mapper::New();
    }
#endif
  
  return new vtkVolumeProVG500Mapper;
}



void vtkVolumeProVG500Mapper::UpdateCamera( vtkRenderer *ren, vtkVolume * vtkNotUsed(vol) )
{
  VLICamera                 *camera;
  VLIMatrix                 *matrixVLI;
  VLIVector3D               *positionVLI;
  VLIVector3D               *focalPointVLI;
  VLIVector3D               *viewUpVLI;
  float                     positionVTK[3];
  float                     focalPointVTK[3];
  float                     viewUpVTK[3];
  VLIStatus                 status;

  matrixVLI = new VLIMatrix();

  // Get the necessary information from the vtk camera
  ren->GetActiveCamera()->GetPosition( positionVTK );
  ren->GetActiveCamera()->GetFocalPoint( focalPointVTK );
  ren->GetActiveCamera()->GetViewUp( viewUpVTK );

  // make sure we are in parallel mode
  if (!ren->GetActiveCamera()->GetParallelProjection())
    {
    vtkWarningMacro("The Volume Pro VG500 does not support perspective projection and the camera is currently not in ParallelProjection mode.");
    }

  // Create the three vectors we need to do the lookat
  positionVLI = new VLIVector3D( positionVTK );
  focalPointVLI = new VLIVector3D( focalPointVTK );
  viewUpVLI = new VLIVector3D( viewUpVTK );

  // Create a camera from this matrix
  camera = new VLICamera();
  status = camera->SetViewMatrix( VLIMatrix::LookAt( *positionVLI, *focalPointVLI, *viewUpVLI ) );

  if ( status != kVLIOK )
    {
    vtkErrorMacro( << "Camera matrix not set!" );
    }

  // Set this as the current camera of the context
  status = this->Context->SetCamera( *camera );
  if ( status != kVLIOK )
    {
    vtkErrorMacro( << "Camera not set!" );
    }


  // Delete everything
  delete positionVLI;
  delete focalPointVLI;
  delete viewUpVLI;
  delete matrixVLI;
  delete camera;

  //  this->Context->SetSuperSamplingSpace( VLICoordinateSpace::kVLICameraSpace );
  
  if ( this->SuperSampling )
    {
    status = this->Context->
      SetSuperSamplingFactor( this->SuperSamplingFactor[0],
                              this->SuperSamplingFactor[1],
                              this->SuperSamplingFactor[2] );
    if ( status != kVLIOK )
      {
      vtkErrorMacro( << "Could not set the supersampling factor!" );
      }
    }
  else
    {
    this->Context->SetSuperSamplingFactor( 1.0, 1.0, 1.0 );
    }
}



void vtkVolumeProVG500Mapper::UpdateLights( vtkRenderer *ren, vtkVolume *vol )
{
  vtkLight     *light;
  float        status;
  int          count;
  int          index;
  float        position[3], focalPoint[3];
  float        intensity;
  VLIVector3D  direction;
  int          i;

  // How many lights do we have?
  count = 0;
  for( ren->GetLights()->InitTraversal(); 
      (light = ren->GetLights()->GetNextItem()); )
    {
    status = light->GetSwitch();
    if ( status > 0.0 )
      {
      count++;
      }
    }

  if ( count > this->NumberOfLights )
    {      
    for ( i = 0; i < this->NumberOfLights; i++ )
      {
      this->Context->RemoveLight( this->Lights[i] );
      this->Lights[i]->Release();
      }
    if ( this->Lights )
      {
      delete [] this->Lights;
      }

    this->NumberOfLights = count;
    this->Lights = new VLILight* [count];

    for ( i = 0; i < this->NumberOfLights; i++ )
      {
      this->Lights[i] =  VLILight::CreateDirectional( );
      this->Context->AddLight( this->Lights[i] );
      }
    }

  index = 0;
  if ( vol->GetProperty()->GetShade() )
    {
    for(ren->GetLights()->InitTraversal(); 
        (light = ren->GetLights()->GetNextItem()); )
      {
      status = light->GetSwitch();
      if ( status > 0.0 )
        {
        light->GetPosition( position );
        light->GetFocalPoint( focalPoint );
        intensity = light->GetIntensity();
        direction.Assign( (focalPoint[0] - position[0]),
                          (focalPoint[1] - position[1]),
                          (focalPoint[2] - position[2]) );
        direction.Normalize();
        this->Lights[index]->SetDirection( direction );
        this->Lights[index]->SetIntensity( intensity );
        index++;
        }
      }
    }
  
  for ( i = index; i < this->NumberOfLights; i++ )
    {
    this->Lights[i]->SetIntensity( 0.0 );
    }
}

void vtkVolumeProVG500Mapper::UpdateProperties( vtkRenderer *vtkNotUsed(ren), 
                                                vtkVolume *vol )
{
  vtkPiecewiseFunction      *grayFunc;
  vtkPiecewiseFunction      *goFunc;
  vtkPiecewiseFunction      *soFunc;
  vtkColorTransferFunction  *rgbFunc;
  VLIuint8                  rgbTable[4096][3];
  VLIuint16                 aTable[4096];
  int                       i;
  float                     scale = 1.0;
  double                    *gradientTable;
  float                     val;

  switch ( this->VolumeDataType )
    {
    case VTK_VOLUME_8BIT:
      scale = 255.0 / 4095.0;
      break;
    case VTK_VOLUME_12BIT_LOWER:
      scale = 1.0;
      break;
    case VTK_VOLUME_12BIT_UPPER:
      scale = 65535.0 / 4095.0;
    }

  soFunc = vol->GetProperty()->GetScalarOpacity();

  switch ( vol->GetProperty()->GetColorChannels() )
    {
    case 1:
      grayFunc = vol->GetProperty()->GetGrayTransferFunction();
      for ( i= 0; i< 4096; i++)
        {
        val = 0.5 + grayFunc->GetValue((float)(i)*scale)*255.0;
        val = (val < 0)?(0):(val);
        val = (val > 255)?(255):(val);
        rgbTable[i][0] = rgbTable[i][1] = rgbTable[i][2] = val;
        
        val = 0.5 + 4095.0 * soFunc->GetValue((float)(i)*scale);
        val = (val < 0)?(0):(val);
        val = (val > 4095)?(4095):(val);
        aTable[i] = val;
        }
      break;
    case 3:
      rgbFunc = vol->GetProperty()->GetRGBTransferFunction();
      for ( i= 0; i< 4096; i++)
        {
        val = 0.5 + rgbFunc->GetRedValue((float)(i)*scale)*255.0;
        val = (val < 0)?(0):(val);
        val = (val > 255)?(255):(val);
        rgbTable[i][0] = val;

        val = 0.5 + rgbFunc->GetGreenValue((float)(i)*scale)*255.0;
        val = (val < 0)?(0):(val);
        val = (val > 255)?(255):(val);
        rgbTable[i][1] = val;

        val = 0.5 + rgbFunc->GetBlueValue((float)(i)*scale)*255.0;
        val = (val < 0)?(0):(val);
        val = (val > 255)?(255):(val);
        rgbTable[i][2] = val;

        val = 0.5 + 4095.0 * soFunc->GetValue((float)(i)*scale);
        val = (val < 0)?(0):(val);
        val = (val > 4095)?(4095):(val);
        aTable[i] = val;
        }
      break;
    }

  this->LookupTable->SetColorEntries( 0, 4096, rgbTable );
  this->LookupTable->SetAlphaEntries( 0, 4096, aTable );

  // Set up the gradient magnitude opacity modulation
  goFunc = vol->GetProperty()->GetGradientOpacity();

  if ( !this->GradientOpacityModulation || !goFunc ||
       ( !strcmp(goFunc->GetType(), "Constant") && 
         goFunc->GetValue(0) == 1.0 ))
    {
    this->Context->SetGradientOpacityModulation( VLIfalse );
    }
  else
    {
    switch ( this->VolumeDataType )
      {
      case VTK_VOLUME_8BIT:
        scale = sqrt(3.0)*256.0;
        break;
      case VTK_VOLUME_12BIT_LOWER:
        scale = sqrt(3.0)*4096;
        break;
      case VTK_VOLUME_12BIT_UPPER:
        scale = sqrt(3.0)*65536;
      }

    gradientTable = new double [this->GradientTableSize];
    float *spacing = this->GetInput()->GetSpacing();
    float avgSpacing = 0.333*(spacing[0] + spacing[1] + spacing[2]);
    scale = scale/(avgSpacing*(this->GradientTableSize-1));
    
    for ( i = 0; i < this->GradientTableSize; i++ )
      {
      // Take an average of five values in the region
      gradientTable[i] = 0.2 * ( 
        goFunc->GetValue(scale*((float)i - 0.4))  +
        goFunc->GetValue(scale*((float)i-0.2)) +
        goFunc->GetValue(scale*((float)i)) +
        goFunc->GetValue(scale*((float)i+0.2)) +
        goFunc->GetValue(scale*((float)i+0.4)));
      }
    
    this->Context->SetGradientOpacityModulation( VLItrue );
    this->Context->SetGradientTable( gradientTable );
    delete [] gradientTable;
    }

  if ( vol->GetProperty()->GetShade() )
    {
    this->Context->
      SetReflectionProperties( vol->GetProperty()->GetDiffuse(),
                               vol->GetProperty()->GetSpecular(),
                               vol->GetProperty()->GetAmbient(),
                               vol->GetProperty()->GetSpecularPower() );
    }
  else
    {
    this->Context->SetReflectionProperties( 0.0, 0.0, 1.0, 1.0 );
    }

  this->Context->SetLookupTable(this->LookupTable); 

  // Now the volume matrix. We have to get the composite matrix
  // out of the volume (both the scale, position, and orientation, and
  // the user matrix) then we need to scale by the spacing of the
  // data.


}

void vtkVolumeProVG500Mapper::UpdateCropping( vtkRenderer * vtkNotUsed(ren), vtkVolume * vtkNotUsed(vol) )
{
  VLICrop  *crop;

  crop = new VLICrop;

  crop->SetSlabs( this->VoxelCroppingRegionPlanes[0], 
                  this->VoxelCroppingRegionPlanes[1],
                  this->VoxelCroppingRegionPlanes[2], 
                  this->VoxelCroppingRegionPlanes[3],
                  this->VoxelCroppingRegionPlanes[4], 
                  this->VoxelCroppingRegionPlanes[5] );

  if ( !this->Cropping )
    {
    crop->SetFlags( VLICrop::kDisable );
    }
  else
    {
    switch ( this->CroppingRegionFlags )
      {
      case VTK_CROP_SUBVOLUME:
        crop->SetFlags( VLICrop::kSubVolume );
        break;
      case VTK_CROP_FENCE:
        crop->SetFlags( VLICrop::k3DFence );
        break;
      case VTK_CROP_INVERTED_FENCE:
        crop->SetFlags( VLICrop::k3DFenceInvert );
        break;
      case VTK_CROP_CROSS:
        crop->SetFlags( VLICrop::k3DCross );
        break;
      case VTK_CROP_INVERTED_CROSS:
        crop->SetFlags( VLICrop::k3DCrossInvert );
        break;
      default:
        crop->SetFlags( VLICrop::kDisable );
        vtkErrorMacro( << "Unsupported crop option!" );
        break;
      }
    }

  this->Context->SetCrop( *crop );

  delete crop;
}

void vtkVolumeProVG500Mapper::UpdateCutPlane( vtkRenderer * vtkNotUsed(ren), vtkVolume *vtkNotUsed(vol) )
{
  VLIStatus   status;

  // If the cut plane is turned off, but the context has a cut plane,
  // then we need to remove it
  if ( !this->CutPlane )
    {
    // Remove it if necessary
    if ( this->Context->GetCutPlaneCount() > 0 )
      {
      status = this->Context->RemoveCutPlane( this->Cut );
      if ( status != kVLIOK )
        {
        vtkErrorMacro( << "Could not remove cut plane from context" );
        }
      }
    }
  // If the cut plane is turned on, and the context does not have a cut
  // plane, then we need to add it. Also, update the position/orientation
  // and thickness of the plane
  else
    {
    // Update the position/orientation
    status = this->Cut->SetPlane( this->CutPlaneEquation[0],
                                  this->CutPlaneEquation[1],
                                  this->CutPlaneEquation[2],
                                  this->CutPlaneEquation[3] );
    if ( status != kVLIOK )
      {
      vtkErrorMacro( << "Could not set cut plane equation" );
      }

    // Update the thickness
    status = this->Cut->SetThickness( this->CutPlaneThickness );
    if ( status != kVLIOK )
      {
      vtkErrorMacro( << "Could not set cut plane thickness" );
      }

    // Update the falloff distance
    status = this->Cut->SetFallOff( this->CutPlaneFallOffDistance );
    if ( status != kVLIOK )
      {
      vtkErrorMacro( << "Could not set cut plane fall off distance" );
      }

    // Add it if necessary
    if ( this->Context->GetCutPlaneCount() == 0 )
      {
      status = this->Context->AddCutPlane( this->Cut );
      if ( status != kVLIOK )
        {
        vtkErrorMacro( << "Could not remove cut plane from context" );
        }
      }
    }
}

void vtkVolumeProVG500Mapper::UpdateCursor( vtkRenderer *vtkNotUsed(ren), vtkVolume *vtkNotUsed(vol) )
{
  VLICursor     *cursor;

  cursor = new VLICursor;

  if ( this->Cursor == 0 )
    {
    cursor->SetAttributes( VLICursor::kDisable );
    }
  else
    {
    cursor->SetAttributes( VLICursor::kEnableAll | VLICursor::kDisableCrop );
    cursor->SetPosition( this->CursorPosition[0], 
                         this->CursorPosition[1],
                         this->CursorPosition[2] );
    
    cursor->SetWidth(1.0);
    
    if ( this->CursorType == VTK_CURSOR_TYPE_CROSSHAIR )
      {
      cursor->SetType( VLICursor::kCrossHair );
      }
    else if ( this->CursorType == VTK_CURSOR_TYPE_PLANE )
      {
      cursor->SetType( VLICursor::kPlane );
      }
    
    cursor->SetColor( VLICursor::kXAxis, 
                      this->CursorXAxisColor[0],
                      this->CursorXAxisColor[1],
                      this->CursorXAxisColor[2] );
    
    cursor->SetColor( VLICursor::kYAxis, 
                      this->CursorYAxisColor[0],
                      this->CursorYAxisColor[1],
                      this->CursorYAxisColor[2] );
    
    cursor->SetColor( VLICursor::kZAxis, 
                      this->CursorZAxisColor[0],
                      this->CursorZAxisColor[1],
                      this->CursorZAxisColor[2] );
                      
    }

  this->Context->SetCursor( *cursor );
  
  delete cursor;
}

void vtkVolumeProVG500Mapper::UpdateVolume( vtkRenderer * vtkNotUsed(ren), vtkVolume * vol )
{
  int                       dataSize[3];
  int                       dataType;
  unsigned char             *uc_data_ptr;
  unsigned short            *us_data_ptr;
  void                      *data_ptr;
  vtkImageData              *input = this->GetInput();
  vtkTransform              *volumeTransform;
  vtkTransform              *scalarTransform;
  int                       i, j;
  double                    matrixValues[16];
  VLIMatrix                 *matrixVLI;
  float                     dataOrigin[3];
  float                     dataSpacing[3];
  VLIStatus                 status;
  float                     range[2];


  // We need the size to create the volume and check the subvolume
  input->GetDimensions( dataSize );

  // If we have a volume, the size still matches, but our data has
  // been modified, call UpdateVolume() to change the content
  if ( this->Volume &&
       input == this->VolumeInput &&
       input->GetMTime() >= this->VolumeBuildTime->GetMTime() &&
       this->LoadedDataSize[0] == dataSize[0] && 
       this->LoadedDataSize[1] == dataSize[1] &&
       this->LoadedDataSize[2] == dataSize[2] )
    {
    int volumeUpdated = 0;

    // Get the data type and a void * pointer to the data
    dataType = input->GetPointData()->GetScalars()->GetDataType();
    data_ptr = input->GetPointData()->GetScalars()->GetVoidPointer(0);

    // Switch on data type and update the volume
    switch ( dataType )
      {
      case VTK_UNSIGNED_CHAR:
        if ( this->VolumeDataType == VTK_VOLUME_8BIT )
          {
          uc_data_ptr = (unsigned char *) data_ptr;
          this->Volume->UpdateVolume( kVLIVoxelFormatUINT8, uc_data_ptr,
                                      0, 0, 0, 
                                      dataSize[0], 
                                      dataSize[1], 
                                      dataSize[2] );
          volumeUpdated = 1;
          }

      break;

      case VTK_UNSIGNED_SHORT:
        if ( this->VolumeDataType == VTK_VOLUME_12BIT_UPPER ||
             this->VolumeDataType == VTK_VOLUME_12BIT_LOWER )
          {
          us_data_ptr = (unsigned short *) data_ptr;

          // If our scalar range is above 4095 (doesn't fit in 12 bits)
          // then use the upper 12 bits of our 16 bit data, otherwise
          // use the lower 12 bits.
          input->GetPointData()->GetScalars()->GetRange( range );
          if ( range[1] > 4095 )
            {
            this->Volume->UpdateVolume(kVLIVoxelFormatUINT12U, 
                                       us_data_ptr,
                                       0, 0, 0,
                                       dataSize[0], 
                                       dataSize[1], 
                                       dataSize[2]);
            this->VolumeDataType = VTK_VOLUME_12BIT_UPPER;
            }
          else
            {
            this->Volume->UpdateVolume(kVLIVoxelFormatUINT12L, 
                                       us_data_ptr,
                                       0, 0, 0,
                                       dataSize[0], 
                                       dataSize[1], 
                                       dataSize[2]);
            this->VolumeDataType = VTK_VOLUME_12BIT_LOWER;
            }
          volumeUpdated = 1;
          }
        break;

      default:
        vtkErrorMacro( << "You must convert your data to unsigned char " <<
                           "or unsigned short for a VolumePro mapper" );
        break;
      }
    
    if ( volumeUpdated )
      {
      this->VolumeBuildTime->Modified();
      }

    }

  // If we have a volume, it is the one we last built with, and it
  // has not been modified since then, then we don't need to rebuilt
  if ( !this->Volume || 
       input != this->VolumeInput ||
       input->GetMTime() >= this->VolumeBuildTime->GetMTime() )
    {
    // Otherwise, we need to build the volume
      this->VolumeInput = input;
      this->VolumeBuildTime->Modified();

    // If we already have one, get rid of it
    if ( this->Volume )
      {
      this->Volume->Release();
      this->Volume = NULL;
      }

    // Get the data type and a void * pointer to the data

    dataType = input->GetPointData()->GetScalars()->GetDataType();
    data_ptr = input->GetPointData()->GetScalars()->GetVoidPointer(0);

    // Switch on data type and create the volume
    switch ( dataType )
      {
      case VTK_UNSIGNED_CHAR:
        uc_data_ptr = (unsigned char *) data_ptr;
        this->Volume = VLIVolume::Create( kVLIVoxelFormatUINT8, uc_data_ptr,
                                          dataSize[0], dataSize[1], 
                                          dataSize[2] );

        this->VolumeDataType = VTK_VOLUME_8BIT;

      break;

      case VTK_UNSIGNED_SHORT:
        us_data_ptr = (unsigned short *) data_ptr;

        // If our scalar range is above 4095 (doesn't fit in 12 bits)
        // then use the upper 12 bits of our 16 bit data, otherwise
        // use the lower 12 bits.
        input->GetPointData()->GetScalars()->GetRange( range );
        if ( range[1] > 4095 )
          {
          this->Volume = VLIVolume::Create(kVLIVoxelFormatUINT12U, 
                                           us_data_ptr,
                                           dataSize[0], dataSize[1], 
                                           dataSize[2]);
          this->VolumeDataType = VTK_VOLUME_12BIT_UPPER;
          }
        else
          {
          this->Volume = VLIVolume::Create(kVLIVoxelFormatUINT12L, 
                                           us_data_ptr,
                                           dataSize[0], dataSize[1], 
                                           dataSize[2]);
          this->VolumeDataType = VTK_VOLUME_12BIT_LOWER;
          }

        break;

      default:
        vtkErrorMacro( << "You must convert your data to unsigned char " <<
                           "or unsigned short for a VolumePro mapper" );
        break;
      }

    }

  // Keep the data size for our check next time
  this->LoadedDataSize[0] = dataSize[0];
  this->LoadedDataSize[1] = dataSize[1];
  this->LoadedDataSize[2] = dataSize[2];

  // Store the matrix of the volume in a temporary transformation matrix
  volumeTransform = vtkTransform::New();
  volumeTransform->SetMatrix( vol->vtkProp3D::GetMatrix() );

  // Get the origin of the data.  This translation is not accounted for in
  // the volume's matrix, so we must add it in.
  input->GetOrigin( dataOrigin );

  // Get the data spacing.  This scaling is not accounted for in
  // the volume's matrix, so we must add it in.
  input->GetSpacing( dataSpacing );

  // Create a transform that will account for the scaling and translation of
  // the scalar data
  scalarTransform = vtkTransform::New();
  scalarTransform->Identity();
  scalarTransform->Translate(dataOrigin[0], dataOrigin[1], dataOrigin[2]);
  scalarTransform->Scale( dataSpacing[0], dataSpacing[1], dataSpacing[2] );

  // Now concatenate the volume's matrix with this scalar data matrix
  volumeTransform->PostMultiply();
  volumeTransform->Concatenate( scalarTransform->GetMatrix() );

  // Now copy the matrix out (inverted) into an array of doubles
  for ( j = 0; j < 4; j++ )
    for ( i = 0; i < 4; i++ )
      {
      matrixValues[j*4 + i] = 
        volumeTransform->GetMatrix()->GetElement( i, j );
      }

  // Create the VLIMatrix and set the matrix values, then set this as
  // the model matrix of the volume

  matrixVLI = new VLIMatrix( matrixValues );
  status = this->Volume->SetModelMatrix( *matrixVLI );
  if ( status != kVLIOK )
    {
    vtkErrorMacro( << "Error setting the volume matrix: " << status );
    }

  // Delete the objects we created
  volumeTransform->Delete();
  scalarTransform->Delete();
  delete matrixVLI;

  // Update the subvolume if it is reasonable
  if ( this->SubVolume[0] >= 0 && 
       this->SubVolume[2] >= 0 &&
       this->SubVolume[4] >= 0 &&
       this->SubVolume[0] < dataSize[0] &&
       this->SubVolume[2] < dataSize[1] &&
       this->SubVolume[4] < dataSize[2] &&
       this->SubVolume[1] >= this->SubVolume[0] && 
       this->SubVolume[3] >= this->SubVolume[2] &&
       this->SubVolume[5] >= this->SubVolume[4] &&
       this->SubVolume[1] < dataSize[0] &&
       this->SubVolume[3] < dataSize[1] &&
       this->SubVolume[5] < dataSize[2] )
    {
    status = 
      this->Volume->SetActiveSubVolumeOrigin( (unsigned int) this->SubVolume[0],
                                              (unsigned int) this->SubVolume[2],
                                              (unsigned int) this->SubVolume[4] );
    if ( status != kVLIOK )
      {
      vtkErrorMacro( << "Could not set the subvolume origin" );
      }

    status = this->Volume->
      SetActiveSubVolumeSize( (this->SubVolume[1]-this->SubVolume[0]) + 1,
                              (this->SubVolume[3]-this->SubVolume[2]) + 1,
                              (this->SubVolume[5]-this->SubVolume[4]) + 1 );
    if ( status != kVLIOK )
      {
      vtkErrorMacro( << "Could not set the subvolume size" );
      }
    }
}

void vtkVolumeProVG500Mapper::CorrectBasePlaneSize( VLIPixel *basePlane, 
                                               int size[2],
                                               VLIPixel **newBasePlane,
                                               int newSize[2],
                                               VLIVector2D textureCoords[6] )
{
  int    i;
  double extent[4];
  int    imageExtent[4];
  double x, y;
  int    requiredSize[2];
  double newX, newY;
  double aspect[2];

  // look for the extent of the texture coordinates
  extent[0] = extent[2] = 1.0;
  extent[1] = extent[3] = 0.0;
  for ( i = 0; i < 6; i++ )
    {
    x = textureCoords[i].X();
    y = textureCoords[i].Y();
    extent[0] = ( x < extent[0] ) ? ( x ) : ( extent[0] );
    extent[1] = ( x > extent[1] ) ? ( x ) : ( extent[1] );
    extent[2] = ( y < extent[2] ) ? ( y ) : ( extent[2] );
    extent[3] = ( y > extent[3] ) ? ( y ) : ( extent[3] );
    }

  // Now compute what this 0-1 float extent means in pixels
  imageExtent[0] = (int)(extent[0] * (float)size[0]);
  imageExtent[1] = (int)(extent[1] * (float)size[0]);
  imageExtent[2] = (int)(extent[2] * (float)size[1]);
  imageExtent[3] = (int)(extent[3] * (float)size[1]);

  // make sure our image extent is within the original extent
  imageExtent[0] = (imageExtent[0]<0)?(0):(imageExtent[0]);
  imageExtent[1] = (imageExtent[1]>(size[0]-1))?(size[0]-1):(imageExtent[1]);
  imageExtent[2] = (imageExtent[2]<0)?(0):(imageExtent[2]);
  imageExtent[3] = (imageExtent[3]>(size[1]-1))?(size[1]-1):(imageExtent[3]);

  // Turn this image extent back into a floating point extent
  extent[0] = (float) imageExtent[0] / (float) (size[0] - 1);
  extent[1] = (float) imageExtent[1] / (float) (size[0] - 1);
  extent[2] = (float) imageExtent[2] / (float) (size[1] - 1);
  extent[3] = (float) imageExtent[3] / (float) (size[1] - 1);

  // How big a texture do we need
  requiredSize[0] = imageExtent[1] - imageExtent[0] + 1;
  requiredSize[1] = imageExtent[3] - imageExtent[2] + 1;

  // What power of two texture does this fit into
  for ( i = 0; i < 2; i++ )
    {
    newSize[i] = 2;
    while ( requiredSize[i] > newSize[i] )
      {
      newSize[i] *= 2;
      }
    }

  // Because of some problems with the memory returned from the
  // volumePro board, keep the full texture if either of the axes has
  // the same resolutions as the full texture
  if ( newSize[0] == size[0] || newSize[1] == size[1] )
    {
    newSize[0] = size[0];
    newSize[1] = size[1];
    }

  // If this is the size we came in with, do nothing
  if ( newSize[0] == size[0] && newSize[1] == size[1] )
    {
    *newBasePlane = basePlane;
    }
  // Otherwise, create the new texture, copy the old into the new, and
  // change the texture coordinates
  else
    {
    *newBasePlane = new VLIPixel[ newSize[0] * newSize[1] * 4 ];

    // Copy the texture into the new (smaller or bigger) space
    for ( i = 0; i < newSize[1]; i++ )
      {
       memcpy( (*newBasePlane + i*newSize[0]), 
               (basePlane + (imageExtent[2]+i)*size[0] + imageExtent[0]), 
               requiredSize[0]*4*sizeof(VLIPixel) );
      }

    aspect[0] = (float)size[0] / (float)newSize[0];
    aspect[1] = (float)size[1] / (float)newSize[1];

    // Change the texture coordinates
    for ( i = 0; i < 6; i++ )
      {
      newX = (textureCoords[i].X() - extent[0]) * aspect[0];
      newX = (newX < 0.0)?(0.0):(newX);
      newX = (newX > 1.0)?(1.0):(newX);

      newY = (textureCoords[i].Y() - extent[2]) * aspect[1];
      newY = (newY < 0.0)?(0.0):(newY);
      newY = (newY > 1.0)?(1.0):(newY);

      textureCoords[i].Assign(newX, newY);
      }
    }
}

int vtkVolumeProVG500Mapper::GetAvailableBoardMemory()
{
  int               memory;
  VLIConfiguration  *config;

  config = new VLIConfiguration;
  memory = config->GetAvailableMemory( 0 );
  delete config;
  
  return memory;
}

void vtkVolumeProVG500Mapper::GetLockSizesForBoardMemory( unsigned int type,
                                                     unsigned int *xSize,
                                                     unsigned int *ySize,
                                                     unsigned int *zSize )
{
  VLIConfiguration  *config;

  config = new VLIConfiguration;
  config->GetMaxLockedSize( type, *xSize, *ySize, *zSize );
  delete config;
}

void vtkVolumeProVG500Mapper::Render( vtkRenderer *ren, vtkVolume *vol )
{
  int                       baseWidth, baseHeight, imageWidth, imageHeight;
  VLIPixel                  *basePlane, *newBasePlane;
  VLIVector3D               hexagon[6];
  VLIVector2D               textureCoords[6];
  int                       size[2], newSize[2];
  VLIStatus                 status;

  if ( !this->StatusOK() )
    {
    return;
    }


  // make sure that we have scalar input and update the scalar input
  if ( this->GetInput() == NULL ) 
    {
    vtkErrorMacro(<< "No Input!");
    return;
    }
  else
    {    
    this->GetInput()->UpdateInformation();
    this->GetInput()->SetUpdateExtentToWholeExtent();
    this->GetInput()->Update();
    } 

  this->ConvertCroppingRegionPlanesToVoxels();
  
  this->UpdateCamera( ren, vol );

  this->UpdateLights( ren, vol);

  this->UpdateVolume( ren, vol );

  this->UpdateProperties( ren, vol );  

  if ( !this->Volume )
    {
    return;
    }

  this->UpdateCropping( ren, vol );

  this->UpdateCutPlane( ren, vol );

  this->UpdateCursor( ren, vol );


  switch ( this->BlendMode )
    {
    case VTK_BLEND_MODE_COMPOSITE:
      this->Context->SetBlendMode( kVLIBlendFTB );
      break;
    case VTK_BLEND_MODE_MAX_INTENSITY:
      this->Context->SetBlendMode( kVLIBlendMIP );
      break;
    case VTK_BLEND_MODE_MIN_INTENSITY:
      this->Context->SetBlendMode( kVLIBlendMINIP );
      break;
    default:
      vtkErrorMacro( << "Unknown blending mode: " << this->BlendMode );
      break;
    }

  status = this->Context->RenderBasePlane(this->Volume, 0, VLIfalse );
  if ( status != kVLIOK && status != kVLIMultiPass )
    {
    switch ( status )
      {
      case kVLIErrArgument:
        vtkErrorMacro( << "Base plane could not be rendered - bad argument!" );
        break;
      case kVLIErrVolumeInvalid:
        vtkErrorMacro( << "Base plane could not be rendered - invalid volume!" );
        break;
      case kVLIErrAlloc:
        vtkErrorMacro( << "Base plane could not be rendered - not enough resources!" );
        break;
      case kVLIErrBasePlaneAllocation:
        vtkErrorMacro( << "Base plane could not be rendered - could not allocate base plane!" );
        break;
      case kVLIErrAccess:
        vtkErrorMacro( << "Base plane could not be rendered - could not access volume!" );
        break;
      default:
        // Don't report the error - this volume just won't render
        // this error is occurring occasionally in vli 2.0 
        //vtkErrorMacro( << "Base plane could not be rendered - unkown error!" );
        break;    
      }
    
    // Release the base plane for use next time
    this->Context->ReleaseBasePlane( 0 );

    return;
    }

  status = this->Context->FetchBasePlane(0, baseWidth, baseHeight,
                                         imageWidth, imageHeight,
                                         basePlane, hexagon, textureCoords);
  if ( status != kVLIOK )
    {
    vtkErrorMacro( << "Base plane could not be fetched!" );
    return;
    }

  size[0] = baseWidth;
  size[1] = baseHeight;

  this->CorrectBasePlaneSize( basePlane, size, 
                              &newBasePlane, newSize, textureCoords );
  this->RenderHexagon( ren, vol, newBasePlane, 
                       newSize, hexagon, textureCoords );

  // Release the base plane for use next time
  this->Context->ReleaseBasePlane( 0 );

  // Delete it only if was created - if it is the same as basePlane, then
  // we didn't create a new one since basePlane was already a power of 2
  if ( newBasePlane != basePlane )
    {
    delete [] newBasePlane;
    }

}

//----------------------------------------------------------------------------
void vtkVolumeProVG500Mapper::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
}
