You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1396 lines
43 KiB
1396 lines
43 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkVolumeProVP1000Mapper.cxx,v $
|
|
|
|
Copyright (c) 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 "vtkVolumeProVP1000Mapper.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 "vtkOpenGLVolumeProVP1000Mapper.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 <stdio.h>
|
|
#include <math.h>
|
|
|
|
vtkCxxRevisionMacro(vtkVolumeProVP1000Mapper, "$Revision: 1.1 $");
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Needed when we don't use the vtkStandardNewMacro.
|
|
vtkInstantiatorNewMacro(vtkVolumeProVP1000Mapper);
|
|
//----------------------------------------------------------------------------
|
|
|
|
vtkVolumeProVP1000Mapper::vtkVolumeProVP1000Mapper()
|
|
{
|
|
VLIStatus status;
|
|
VLIConfiguration *config;
|
|
|
|
this->ImageBuffer = NULL;
|
|
this->DepthBuffer = NULL;
|
|
|
|
// 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->GetClassifier().SetLookupTable(kVLITable0, 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;
|
|
}
|
|
|
|
this->DrawBoundingBox = 0;
|
|
}
|
|
|
|
|
|
|
|
vtkVolumeProVP1000Mapper::~vtkVolumeProVP1000Mapper()
|
|
{
|
|
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 )
|
|
{
|
|
if (this->Volume->IsLocked() == VLItrue)
|
|
{
|
|
this->Volume->UnlockVolume();
|
|
}
|
|
this->Volume->Release();
|
|
}
|
|
|
|
if (this->ImageBuffer)
|
|
{
|
|
this->ImageBuffer->Release();
|
|
this->ImageBuffer = NULL;
|
|
}
|
|
|
|
if (this->DepthBuffer)
|
|
{
|
|
this->DepthBuffer->Release();
|
|
this->DepthBuffer = NULL;
|
|
}
|
|
|
|
// Free the context if necessary
|
|
if (this->Context)
|
|
{
|
|
this->Context->Release();
|
|
}
|
|
|
|
// Terminate connection to the hardware
|
|
VLIClose();
|
|
}
|
|
|
|
vtkVolumeProVP1000Mapper *vtkVolumeProVP1000Mapper::New()
|
|
{
|
|
// First try to create the object from the vtkObjectFactory
|
|
vtkObject* ret = vtkObjectFactory::CreateInstance("vtkVolumeProVP1000Mapper");
|
|
if(ret)
|
|
{
|
|
return (vtkVolumeProVP1000Mapper*)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("vtkVolumeProVP1000Mapper");
|
|
#endif
|
|
return vtkOpenGLVolumeProVP1000Mapper::New();
|
|
}
|
|
#endif
|
|
#ifdef _WIN32
|
|
if (!strcmp("Win32OpenGL",temp))
|
|
{
|
|
#ifdef VTK_DEBUG_LEAKS
|
|
vtkDebugLeaks::DestructClass("vtkVolumeProVP1000Mapper");
|
|
#endif
|
|
return vtkOpenGLVolumeProVP1000Mapper::New();
|
|
}
|
|
#endif
|
|
|
|
return new vtkVolumeProVP1000Mapper;
|
|
}
|
|
|
|
|
|
|
|
void vtkVolumeProVP1000Mapper::UpdateCamera( vtkRenderer *ren, vtkVolume * vtkNotUsed(vol) )
|
|
{
|
|
double positionVTK[3];
|
|
double focalPointVTK[3];
|
|
double viewUpVTK[3];
|
|
VLIStatus status;
|
|
|
|
// 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 VP1000 does not support perspective projection and the camera is currently not in ParallelProjection mode.");
|
|
}
|
|
|
|
// Create the three vectors we need to do the lookat
|
|
VLIVector3D positionVLI ( positionVTK );
|
|
VLIVector3D focalPointVLI ( focalPointVTK );
|
|
VLIVector3D viewUpVLI ( viewUpVTK );
|
|
|
|
// Create a camera from this matrix
|
|
VLIMatrix viewMatrixVLI = VLIMatrix::LookAt(positionVLI, focalPointVLI,
|
|
viewUpVLI );
|
|
status = this->Context->GetCamera().SetViewMatrix( viewMatrixVLI );
|
|
|
|
double clippingRange[2], parallelScale;
|
|
double aspect[2];
|
|
ren->GetActiveCamera()->GetClippingRange(clippingRange);
|
|
ren->GetAspect(aspect);
|
|
parallelScale = ren->GetActiveCamera()->GetParallelScale();
|
|
|
|
VLIMatrix projectionMatrixVLI = VLIMatrix::Ortho(-parallelScale*aspect[0],
|
|
parallelScale*aspect[0],
|
|
-parallelScale,
|
|
parallelScale,
|
|
clippingRange[0],
|
|
clippingRange[1]);
|
|
|
|
status = this->Context->GetCamera().SetProjectionMatrix( projectionMatrixVLI );
|
|
|
|
if ( status != kVLIOK )
|
|
{
|
|
vtkErrorMacro( << "Camera matrix not set!" );
|
|
}
|
|
|
|
if ( this->SuperSampling )
|
|
{
|
|
if (this->SuperSamplingFactor[2] == 0.0)
|
|
{
|
|
status = this->Context->SetSamplingFactor(1.0);
|
|
}
|
|
else
|
|
{
|
|
status =
|
|
this->Context->SetSamplingFactor(1/this->SuperSamplingFactor[2]);
|
|
}
|
|
if ( status != kVLIOK )
|
|
{
|
|
vtkErrorMacro( << "Could not set the sampling factor!" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this->Context->SetSamplingFactor( 1.0 );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void vtkVolumeProVP1000Mapper::UpdateLights( vtkRenderer *ren, vtkVolume *vol )
|
|
{
|
|
vtkLight *light;
|
|
float status;
|
|
int count;
|
|
int index;
|
|
double 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 vtkVolumeProVP1000Mapper::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_16BIT:
|
|
scale = 65535.0 / 4095.0;
|
|
break;
|
|
}
|
|
|
|
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(static_cast<float>(i)*scale)*255.0;
|
|
val = (val < 0)?(0):(val);
|
|
val = (val > 255)?(255):(val);
|
|
rgbTable[i][0] = rgbTable[i][1] = rgbTable[i][2]
|
|
= static_cast<unsigned char>( val );
|
|
|
|
val = 0.5 + 4095.0 * soFunc->GetValue(static_cast<float>(i)*scale);
|
|
val = (val < 0)?(0):(val);
|
|
val = (val > 4095)?(4095):(val);
|
|
aTable[i] = static_cast<unsigned short>( val );
|
|
}
|
|
break;
|
|
case 3:
|
|
rgbFunc = vol->GetProperty()->GetRGBTransferFunction();
|
|
for ( i= 0; i< 4096; i++)
|
|
{
|
|
val = 0.5 + rgbFunc->GetRedValue(static_cast<float>(i)*scale)*255.0;
|
|
val = (val < 0)?(0):(val);
|
|
val = (val > 255)?(255):(val);
|
|
rgbTable[i][0] = static_cast<unsigned char>( val );
|
|
|
|
val = 0.5 + rgbFunc->GetGreenValue(static_cast<float>(i)*scale)*255.0;
|
|
val = (val < 0)?(0):(val);
|
|
val = (val > 255)?(255):(val);
|
|
rgbTable[i][1] = static_cast<unsigned char>( val );
|
|
|
|
val = 0.5 + rgbFunc->GetBlueValue(static_cast<float>(i)*scale)*255.0;
|
|
val = (val < 0)?(0):(val);
|
|
val = (val > 255)?(255):(val);
|
|
rgbTable[i][2] = static_cast<unsigned char>( val );
|
|
|
|
val = 0.5 + 4095.0 * soFunc->GetValue(static_cast<float>(i)*scale);
|
|
val = (val < 0)?(0):(val);
|
|
val = (val > 4095)?(4095):(val);
|
|
aTable[i] = static_cast<unsigned short>( 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_16BIT:
|
|
scale = sqrt(3.0)*65536;
|
|
break;
|
|
}
|
|
|
|
gradientTable = new double [this->GradientTableSize];
|
|
double *spacing = this->GetInput()->GetSpacing();
|
|
double 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*(static_cast<float>(i - 0.4))) +
|
|
goFunc->GetValue(scale*(static_cast<float>(i-0.2))) +
|
|
goFunc->GetValue(scale*(static_cast<float>(i))) +
|
|
goFunc->GetValue(scale*(static_cast<float>(i+0.2))) +
|
|
goFunc->GetValue(scale*(static_cast<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->GetClassifier().SetLookupTable(kVLITable0, this->LookupTable);
|
|
}
|
|
|
|
void vtkVolumeProVP1000Mapper::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 vtkVolumeProVP1000Mapper::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 vtkVolumeProVP1000Mapper::UpdateCursor( vtkRenderer *vtkNotUsed(ren), vtkVolume *vtkNotUsed(vol) )
|
|
{
|
|
}
|
|
|
|
void vtkVolumeProVP1000Mapper::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 *correctionTransform;
|
|
vtkTransform *modelTransform;
|
|
int i, j;
|
|
double dataOrigin[3];
|
|
double dataSpacing[3];
|
|
VLIStatus status;
|
|
double range[2];
|
|
|
|
// We need the size to create the volume and check the subvolume
|
|
input->GetDimensions( dataSize );
|
|
VLIVolumeRange volumeRange (dataSize[0], dataSize[1], dataSize[2]);
|
|
|
|
// 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 = static_cast<unsigned char *>(data_ptr);
|
|
this->Volume->Update( uc_data_ptr, volumeRange );
|
|
volumeUpdated = 1;
|
|
}
|
|
|
|
break;
|
|
|
|
case VTK_UNSIGNED_SHORT:
|
|
if ( this->VolumeDataType == VTK_VOLUME_16BIT ||
|
|
this->VolumeDataType == VTK_VOLUME_12BIT_LOWER)
|
|
{
|
|
us_data_ptr = static_cast<unsigned short *>(data_ptr);
|
|
this->Volume->Update(us_data_ptr, volumeRange);
|
|
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 = static_cast<unsigned char *>(data_ptr);
|
|
this->Volume = VLIVolume::Create( 8, dataSize[0], dataSize[1],
|
|
dataSize[2], 0, 0, uc_data_ptr );
|
|
this->Volume->SetFieldDescriptor(kVLIField0,
|
|
VLIFieldDescriptor(0, 8, kVLIUnsignedFraction));
|
|
|
|
this->VolumeDataType = VTK_VOLUME_8BIT;
|
|
|
|
break;
|
|
|
|
case VTK_UNSIGNED_SHORT:
|
|
us_data_ptr = static_cast<unsigned short *>(data_ptr);
|
|
this->Volume = VLIVolume::Create( 16, dataSize[0], dataSize[1],
|
|
dataSize[2], 0, 0, us_data_ptr );
|
|
|
|
input->GetPointData()->GetScalars()->GetRange( range );
|
|
if ( range[1] > 4095 )
|
|
{
|
|
this->Volume->SetFieldDescriptor(kVLIField0,
|
|
VLIFieldDescriptor(0, 16, kVLIUnsignedFraction));
|
|
|
|
this->VolumeDataType = VTK_VOLUME_16BIT;
|
|
}
|
|
else
|
|
{
|
|
this->Volume->SetFieldDescriptor(kVLIField0,
|
|
VLIFieldDescriptor(0, 12, kVLIUnsignedFraction));
|
|
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
|
|
modelTransform = vtkTransform::New();
|
|
modelTransform->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
|
|
correctionTransform = vtkTransform::New();
|
|
correctionTransform->Identity();
|
|
correctionTransform->Translate(dataOrigin[0], dataOrigin[1], dataOrigin[2]);
|
|
correctionTransform->Scale( dataSpacing[0], dataSpacing[1], dataSpacing[2] );
|
|
|
|
VLIMatrix correctionMatrixVLI;
|
|
VLIMatrix modelMatrixVLI;
|
|
|
|
// Now copy the matrix out (inverted) into an array of doubles
|
|
for ( j = 0; j < 4; j++ )
|
|
for ( i = 0; i < 4; i++ )
|
|
{
|
|
modelMatrixVLI[i][j] = modelTransform->GetMatrix()->GetElement( i, j );
|
|
correctionMatrixVLI[i][j] = correctionTransform->GetMatrix()->GetElement( i, j );
|
|
}
|
|
|
|
if( this->Volume )
|
|
{
|
|
status = this->Volume->SetCorrectionMatrix( correctionMatrixVLI );
|
|
if ( status != kVLIOK )
|
|
{
|
|
vtkErrorMacro( << "Error setting the correction matrix: " << status );
|
|
}
|
|
}
|
|
|
|
status = this->Context->GetCamera().SetModelMatrix( modelMatrixVLI );
|
|
if ( status != kVLIOK )
|
|
{
|
|
vtkErrorMacro( << "Error setting the model matrix: " << status );
|
|
}
|
|
|
|
// Delete the objects we created
|
|
correctionTransform->Delete();
|
|
modelTransform->Delete();
|
|
|
|
// Update the subvolume if it is reasonable
|
|
if ( this->Volume &&
|
|
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] )
|
|
{
|
|
VLIVolumeRange volRange ((this->SubVolume[1]-this->SubVolume[0]) + 1,
|
|
(this->SubVolume[3]-this->SubVolume[2]) + 1,
|
|
(this->SubVolume[5]-this->SubVolume[4]) + 1,
|
|
this->SubVolume[0], this->SubVolume[2],
|
|
this->SubVolume[4] );
|
|
status =
|
|
this->Volume->SetActiveSubVolume( volRange );
|
|
if ( status != kVLIOK )
|
|
{
|
|
vtkErrorMacro( << "Could not set the active subvolume" );
|
|
}
|
|
}
|
|
}
|
|
|
|
int vtkVolumeProVP1000Mapper::GetAvailableBoardMemory()
|
|
{
|
|
int memory;
|
|
VLIConfiguration *config;
|
|
|
|
config = new VLIConfiguration;
|
|
memory = config->GetAvailableMemory( 0 );
|
|
delete config;
|
|
|
|
return memory;
|
|
}
|
|
|
|
void vtkVolumeProVP1000Mapper::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 vtkVolumeProVP1000Mapper::Render( vtkRenderer *ren, vtkVolume *vol )
|
|
{
|
|
int size[2];
|
|
VLIStatus status;
|
|
//return;
|
|
|
|
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 );
|
|
|
|
this->Context->SetCorrectGradient (VLItrue);
|
|
|
|
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;
|
|
}
|
|
|
|
int *windowSize;
|
|
windowSize = ren->GetRenderWindow()->GetSize();
|
|
|
|
status = this->Volume->LockVolume();
|
|
|
|
if ( this->ImageBuffer )
|
|
{
|
|
unsigned int width, height;
|
|
this->ImageBuffer->GetSize(width, height);
|
|
if (static_cast<int>(width) != windowSize[0] ||
|
|
static_cast<int>(height) != windowSize[1])
|
|
{
|
|
this->ImageBuffer->Release();
|
|
this->ImageBuffer = NULL;
|
|
}
|
|
}
|
|
if ( ! this->ImageBuffer )
|
|
{
|
|
static VLIFieldDescriptor sImageBufferFields[4] =
|
|
{
|
|
VLIFieldDescriptor(0, 8, kVLIUnsignedFraction),
|
|
VLIFieldDescriptor(8, 8, kVLIUnsignedFraction),
|
|
VLIFieldDescriptor(16, 8, kVLIUnsignedFraction),
|
|
VLIFieldDescriptor(24, 8, kVLIUnsignedFraction)
|
|
};
|
|
|
|
this->ImageBuffer = VLIImageBuffer::Create(kVLIBoard0, windowSize[0],
|
|
windowSize[1], 32, 4,
|
|
sImageBufferFields);
|
|
this->ImageBuffer->SetBorderValue(0, 0, 0, 0);
|
|
}
|
|
|
|
this->Context->SetRayTermination(1.0, VLIfalse);
|
|
|
|
int width = 0, height = 0;
|
|
|
|
this->CheckSubSampling(this->Volume, this->Context, width, height);
|
|
|
|
int imageWidth, imageHeight;
|
|
imageWidth = this->ImageBuffer->GetWidth();
|
|
imageHeight = this->ImageBuffer->GetHeight();
|
|
|
|
this->DrawBoundingBox = 0;
|
|
|
|
if (width > imageWidth || height > imageHeight)
|
|
{
|
|
if (width < 2000 && height < 2000)
|
|
{
|
|
float aspectRatio = (float)imageWidth / (float)imageHeight;
|
|
int widthDiff, heightDiff, newWidth, newHeight;
|
|
float increase;
|
|
|
|
widthDiff = width - imageWidth;
|
|
heightDiff = height - imageHeight;
|
|
if (widthDiff > heightDiff)
|
|
{
|
|
increase = (float)width / (float)imageWidth;
|
|
newWidth = width;
|
|
newHeight = ceil(imageHeight*increase);
|
|
}
|
|
else
|
|
{
|
|
increase = (float)height / (float)imageHeight;
|
|
newWidth = ceil(imageWidth*increase);
|
|
newHeight = height;
|
|
}
|
|
this->ImageBuffer->Release();
|
|
static VLIFieldDescriptor sImageBufferFields[4] =
|
|
{
|
|
VLIFieldDescriptor(0, 8, kVLIUnsignedFraction),
|
|
VLIFieldDescriptor(8, 8, kVLIUnsignedFraction),
|
|
VLIFieldDescriptor(16, 8, kVLIUnsignedFraction),
|
|
VLIFieldDescriptor(24, 8, kVLIUnsignedFraction)
|
|
};
|
|
|
|
this->ImageBuffer = VLIImageBuffer::Create(kVLIBoard0, newWidth,
|
|
newHeight, 32, 4,
|
|
sImageBufferFields);
|
|
this->ImageBuffer->SetBorderValue(0, 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
this->DrawBoundingBox = 1;
|
|
}
|
|
}
|
|
|
|
if ( ! this->DrawBoundingBox)
|
|
{
|
|
if ( ! this->IntermixIntersectingGeometry )
|
|
{
|
|
status = this->Volume->Render(this->Context, this->ImageBuffer);
|
|
}
|
|
else
|
|
{
|
|
VLIImageRange iRange = VLIImageRange(windowSize[0], windowSize[1]);
|
|
if ( this->DepthBuffer )
|
|
{
|
|
unsigned int width, height;
|
|
this->DepthBuffer->GetSize(width, height);
|
|
if (static_cast<int>(width) != windowSize[0] ||
|
|
static_cast<int>(height) != windowSize[1])
|
|
{
|
|
this->DepthBuffer->Release();
|
|
this->DepthBuffer = NULL;
|
|
}
|
|
}
|
|
if ( ! this->DepthBuffer )
|
|
{
|
|
this->DepthBuffer = VLIDepthBuffer::Create(kVLIBoard0, windowSize[0],
|
|
windowSize[1]);
|
|
this->DepthBuffer->SetBorderValue(0);
|
|
this->DepthBuffer->SetInputLimits(iRange);
|
|
status = this->Context->SetDepthTest(VLIContext::kDepthBuffer1,
|
|
VLIContext::kDepthTestLess);
|
|
}
|
|
unsigned int *depthData = new unsigned int[windowSize[0]*windowSize[1]];
|
|
this->GetDepthBufferValues(ren, windowSize, depthData);
|
|
|
|
status = this->DepthBuffer->Update(depthData,
|
|
VLIImageRange(windowSize[0],
|
|
windowSize[1]));
|
|
if ( status != kVLIOK )
|
|
{
|
|
switch ( status )
|
|
{
|
|
case kVLIErrArgument:
|
|
vtkErrorMacro( << "Invalid argument for updating depth buffer!" );
|
|
break;
|
|
case kVLIErrAlloc:
|
|
vtkErrorMacro( << "Not enough resources to update depth buffer!" );
|
|
break;
|
|
default:
|
|
// Don't know what the error is, but can't update the depth buffer.
|
|
// Shouldn't get to this error message.
|
|
vtkErrorMacro( << "Unknown error updating depth buffer!" );
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
this->ImageBuffer->Clear(iRange, 0);
|
|
status = this->Volume->Render(this->Context, this->ImageBuffer, 0, 0,
|
|
this->DepthBuffer);
|
|
|
|
delete [] depthData;
|
|
}
|
|
|
|
if ( status != kVLIOK )
|
|
{
|
|
switch ( status )
|
|
{
|
|
case kVLIErrArgument:
|
|
vtkErrorMacro( << "Volume could not be rendered - bad argument!" );
|
|
break;
|
|
case kVLIErrCantSubsample:
|
|
vtkErrorMacro( << "Volume could not be rendered - volume too large for viewport!");
|
|
break;
|
|
case kVLIErrClassifier:
|
|
vtkErrorMacro( << "Volume could not be rendered - invalid classifier!");
|
|
break;
|
|
case kVLIErrTransform:
|
|
vtkErrorMacro( << "Volume could not be rendered - invalid transform state!");
|
|
break;
|
|
case kVLIErrAccess:
|
|
vtkErrorMacro( << "Volume could not be rendered - could not access volume!" );
|
|
break;
|
|
case kVLIErrPermission:
|
|
vtkErrorMacro( << "Volume could not be rendered - do not have permission to perform render!");
|
|
break;
|
|
case kVLIErrVolume:
|
|
vtkErrorMacro( << "Volume could not be rendered - no attached buffer!");
|
|
break;
|
|
case kVLIErrAlloc:
|
|
vtkErrorMacro( << "Volume could not be rendered - not enough resources!" );
|
|
break;
|
|
default:
|
|
// Don't report the error - this volume just won't render
|
|
vtkErrorMacro( << "Volume could not be rendered - unkown error!" );
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
size[0] = this->ImageBuffer->GetWidth();
|
|
size[1] = this->ImageBuffer->GetHeight();
|
|
|
|
unsigned int *outData = new unsigned int[size[0]*size[1]];
|
|
|
|
status = this->ImageBuffer->Unload(outData,
|
|
this->ImageBuffer->GetOutputLimits());
|
|
|
|
if ( status != kVLIOK )
|
|
{
|
|
switch (status)
|
|
{
|
|
case kVLIErrArgument:
|
|
vtkErrorMacro("Image buffer could not be unloaded - invalid argument!");
|
|
break;
|
|
case kVLIErrAlloc:
|
|
vtkErrorMacro("Image buffer could not be unloaded - not enough resources!");
|
|
break;
|
|
case kVLIErrInternal:
|
|
vtkErrorMacro("Image buffer could not be unloaded - internal VLI error!");
|
|
break;
|
|
default:
|
|
vtkErrorMacro("Image buffer could not be unloaded - unknown error!");
|
|
}
|
|
}
|
|
|
|
// Render the image buffer we've been returned.
|
|
this->RenderImageBuffer(ren, vol, size, outData);
|
|
|
|
delete [] outData;
|
|
}
|
|
else
|
|
{
|
|
this->RenderBoundingBox(ren, vol);
|
|
}
|
|
}
|
|
|
|
#if ((VTK_MAJOR_VERSION == 3)&&(VTK_MINOR_VERSION == 2))
|
|
void vtkVolumeProVP1000Mapper::ConvertCroppingRegionPlanesToVoxels()
|
|
{
|
|
memcpy( this->VoxelCroppingRegionPlanes, this->CroppingRegionPlanes,
|
|
sizeof ( this->VoxelCroppingRegionPlanes ) );
|
|
}
|
|
#endif
|
|
|
|
VLIStatus vtkVolumeProVP1000Mapper::CheckSubSampling(const VLIVolume *inVolume,
|
|
const VLIContext *inContext,
|
|
int &outMinImageWidth,
|
|
int &outMinImageHeight)
|
|
{
|
|
enum VGAxis
|
|
{
|
|
kU = 0,
|
|
kV = 1,
|
|
kW = 2
|
|
};
|
|
|
|
enum VGNeg
|
|
{
|
|
kNotNeg = 0,
|
|
kIsNeg = 1
|
|
};
|
|
|
|
const double kEpsilonSubSample = 1.999;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 1) Initialize and calculate matrices:
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
VLIStatus status = kVLIOK;
|
|
|
|
double depthNear, depthFar;
|
|
int viewportMinX, viewportMinY, viewportWidth, viewportHeight;
|
|
inContext->GetCamera().GetViewport(viewportMinX, viewportMinY,
|
|
viewportWidth, viewportHeight);
|
|
inContext->GetCamera().GetDepthRange(depthNear, depthFar);
|
|
|
|
if (viewportWidth <=0 || viewportHeight <=0)
|
|
{
|
|
viewportWidth = 3;
|
|
viewportHeight = 3;
|
|
viewportMinX = 0;
|
|
viewportMinY = 0;
|
|
status = kVLIErrCantSubsample;
|
|
}
|
|
|
|
//------------------------------------------------------
|
|
// Calculate viewport matrix from viewport paramteres
|
|
//------------------------------------------------------
|
|
|
|
VLIMatrix viewportMatrix;
|
|
viewportMatrix.Assign(
|
|
(viewportWidth -1)/2.0, 0, 0, (viewportWidth -1)/2.0 + viewportMinX,
|
|
0, (viewportHeight-1)/2.0, 0, (viewportHeight-1)/2.0 + viewportMinY,
|
|
0, 0, (depthFar-depthNear)/2.0, (depthFar+depthNear)/2.0,
|
|
0, 0, 0, 1.0);
|
|
|
|
//------------------------------------------------------
|
|
// Calculate viewport-viewmapping(projection)-CorrectedModelView matrix
|
|
//-------------------------------------------------------
|
|
|
|
VLIMatrix projection = inContext->GetCamera().GetProjectionMatrix();
|
|
VLIMatrix viewMatrix = inContext->GetCamera().GetViewMatrix();
|
|
VLIMatrix model = inContext->GetCamera().GetModelMatrix();
|
|
VLIMatrix correction = inVolume->GetCorrectionMatrix();
|
|
|
|
VLIMatrix VP_VM_CRMVMatrix =
|
|
viewportMatrix *
|
|
inContext->GetCamera().GetProjectionMatrix() *
|
|
inContext->GetCamera().GetViewMatrix() *
|
|
inContext->GetCamera().GetModelMatrix() *
|
|
inVolume->GetCorrectionMatrix();
|
|
|
|
if (VP_VM_CRMVMatrix.IsSingular())
|
|
{
|
|
return kVLIErrTransform;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 2) Calculate permutation matrix
|
|
// a) Choose primary axis to permuted space
|
|
// b) Decide the select and neg values in transform
|
|
// c) Construct permutation matrix considering min block
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//------------------------------------------------------
|
|
// a) Choose primary axis to permuted space,
|
|
//
|
|
// The Z axis of the permuted space
|
|
// is one axis of object space that is
|
|
// closest to the casting ray direction
|
|
//------------------------------------------------------
|
|
|
|
|
|
VLIVector4D view(0.0, 0.0, 1.0, 0.0); // view vector in image space
|
|
|
|
VLIMatrix VP_VM_CRMVInverse = VP_VM_CRMVMatrix.Inverse();
|
|
|
|
VLIVector4D viewVectorInObjectSpace = (VP_VM_CRMVInverse * view).Normalize();
|
|
|
|
double max = fabs (viewVectorInObjectSpace[0]);
|
|
VGAxis primaryAxis = kU;
|
|
|
|
if (fabs (viewVectorInObjectSpace[1]) > max)
|
|
{
|
|
primaryAxis = kV;
|
|
max = fabs(viewVectorInObjectSpace[1]);
|
|
}
|
|
|
|
if (fabs (viewVectorInObjectSpace[2]) > max)
|
|
{
|
|
primaryAxis = kW;
|
|
max = fabs(viewVectorInObjectSpace[2]);
|
|
}
|
|
|
|
//------------------------------------------------------
|
|
// b) Decide the select and neg values in transform
|
|
//
|
|
// selectZ is the primary axis, negZ as its direction
|
|
//
|
|
// selectX, selectY is chosen
|
|
// to keep the coordinate system order
|
|
//------------------------------------------------------
|
|
|
|
int dirSignOfViewVector[3]; // sign of du, dv, dw direction
|
|
int i;
|
|
for ( i = 0; i < 3 ; i++)
|
|
{
|
|
dirSignOfViewVector[i] =(viewVectorInObjectSpace[i] < 0 )? -1:1;
|
|
}
|
|
|
|
// even: 1, odd -1
|
|
int even = dirSignOfViewVector[0] * dirSignOfViewVector[1]*
|
|
dirSignOfViewVector[2];
|
|
|
|
|
|
int axisObj = primaryAxis; // axis of Object Space
|
|
int negSign[3];
|
|
|
|
// SelectZ, negZ
|
|
VGAxis select[3];
|
|
VGNeg neg[3];
|
|
select[2] = primaryAxis;
|
|
neg[2] = (dirSignOfViewVector[axisObj] == 1)?kNotNeg:kIsNeg;
|
|
negSign[2] = dirSignOfViewVector[axisObj];
|
|
|
|
// SelectX, SelectY, negX, negY
|
|
int axisP; // Axis of Permuted Space
|
|
|
|
// The permuted axes are chosen to keep the same coordinate order
|
|
// (right or left) as object space
|
|
for ( i = 1; i < 3; i++)
|
|
{
|
|
axisP = (2+ i* even)%3;
|
|
axisObj = (primaryAxis +i) %3;
|
|
|
|
select[axisP] = (VGAxis)(axisObj);
|
|
neg[axisP] = (dirSignOfViewVector[axisObj] == 1)?kNotNeg:kIsNeg;
|
|
negSign[axisP] = dirSignOfViewVector[axisObj];
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// c) Construct permutation matrix considering mini block
|
|
//------------------------------------------------------------
|
|
|
|
#define SELECT_AXIS(select, axisObj) ((select == axisObj)?1:0)
|
|
#define PERMUTE(axisP,axisObj) negSign[axisP] * SELECT_AXIS(select[axisP], axisObj)
|
|
|
|
// shift -1 if du, dv, dw < 0
|
|
#define SHIFT(index) ((negSign[index]==1)? 0:-1)
|
|
VLIMatrix permutation;
|
|
permutation.Assign(
|
|
PERMUTE(0,kU), PERMUTE(0,kV),PERMUTE(0,kW), SHIFT(0),
|
|
PERMUTE(1,kU), PERMUTE(1,kV),PERMUTE(1,kW), SHIFT(1),
|
|
PERMUTE(2,kU), PERMUTE(2,kV),PERMUTE(2,kW), SHIFT(2),
|
|
0, 0, 0, 1);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// 3) Calculate sample space increment registers using matrices
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
VLIMatrix VP_VM_CRMVPermuted = VP_VM_CRMVMatrix * permutation.Inverse();
|
|
|
|
// 3.a) The 2x2 upper part of dI_DV Matrix is same in dS_dV Matrix
|
|
|
|
VLIMatrix dS_dVsubMatrix = VLIMatrix::Identity();
|
|
|
|
dS_dVsubMatrix[0][0] = VP_VM_CRMVPermuted[0][0];
|
|
dS_dVsubMatrix[0][1] = VP_VM_CRMVPermuted[0][1];
|
|
dS_dVsubMatrix[1][0] = VP_VM_CRMVPermuted[1][0];
|
|
dS_dVsubMatrix[1][1] = VP_VM_CRMVPermuted[1][1];
|
|
|
|
VLIMatrix dV_dSsubMatrix = dS_dVsubMatrix.Inverse();
|
|
|
|
dV_dSsubMatrix[0][0] = fabs(dV_dSsubMatrix[0][0]);
|
|
dV_dSsubMatrix[0][1] = fabs(dV_dSsubMatrix[0][1]);
|
|
dV_dSsubMatrix[1][0] = fabs(dV_dSsubMatrix[1][0]);
|
|
dV_dSsubMatrix[1][1] = fabs(dV_dSsubMatrix[1][1]);
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// A Scaling method that will
|
|
//
|
|
// 1) Change only the image size, (viewport)
|
|
//
|
|
// 2) keep the same permutation matrix
|
|
//
|
|
// 3) keep valid DepthWarp Matrix
|
|
//
|
|
// 4) avoid sub sampling, so that
|
|
//
|
|
// VLIAbs(dXv_dXs) + VLIAbs(dXv_dYs) <=2
|
|
// VLIAbs(dYv_dXs) + VLIAbs(dYv_dYs) <=2
|
|
//
|
|
// To conclude, get a scaleX, scaleY in imageSize
|
|
// so that
|
|
// VLIAbs(dXv_dXs)/ScaleX+ VLIAbs(dXv_dYs)/ScaleY <=2
|
|
// VLIAbs(dXv_dYs)/ScaleX+ VLIAbs(dYv_dYs)/ScaleY <=2
|
|
//
|
|
// Different ways to choose ScaleX, ScaleY to avoid sub Sampling
|
|
// We choose here when ScaleX == ScaleY, and most close to
|
|
// ScaleX =1 , ScaleY =1
|
|
//
|
|
|
|
double dXvSampleMax= dV_dSsubMatrix[0][0] + dV_dSsubMatrix[0][1];
|
|
double dYvSampleMax= dV_dSsubMatrix[1][0] + dV_dSsubMatrix[1][1];
|
|
|
|
if (dXvSampleMax > 2.0 || dYvSampleMax > 2.0)
|
|
{
|
|
status = kVLIErrCantSubsample;
|
|
}
|
|
|
|
double viewportScale = dXvSampleMax > dYvSampleMax? dXvSampleMax:dYvSampleMax;
|
|
|
|
viewportScale /= kEpsilonSubSample;
|
|
//*************************************************
|
|
//Solution I:
|
|
//double dXvSampleMax= dV_dSsubMatrix[0][0] > dV_dSsubMatrix[1][0]? dV_dSsubMatrix[0][0]:dV_dSsubMatrix[1][0];
|
|
//double dYvSampleMax= dV_dSsubMatrix[0][1] > dV_dSsubMatrix[1][1]? dV_dSsubMatrix[0][1]:dV_dSsubMatrix[1][1];
|
|
//**************************************
|
|
|
|
//*******************************
|
|
//Solution II:
|
|
/*Not proper to choose the interesection of the two equation!!!!
|
|
VLIVector4D imageScale(2,2,0,0);
|
|
dS_dVsubMatrix = dV_dSsubMatrix.Inverse();
|
|
imageScale = dS_dVsubMatrix * imageScale;
|
|
|
|
outMinImageWidth = (int)ceil( (viewportWidth -1)/imageScale[0] +1);
|
|
outMinImageHeight = (int)ceil( (viewportHeight-1)/imageScale[1] +1);*/
|
|
|
|
// Instead of using scaling dXvSampleMax/2.0, using dZvSampleMax/kEpsionSubSample
|
|
outMinImageWidth = (int)ceil( double (viewportWidth -1)*viewportScale +1);
|
|
outMinImageHeight = (int)ceil( double (viewportHeight-1)*viewportScale +1);
|
|
|
|
return status;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkVolumeProVP1000Mapper::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os,indent);
|
|
}
|
|
|