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.
1177 lines
34 KiB
1177 lines
34 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkVolumeTextureMapper2D.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 "vtkVolumeTextureMapper2D.h"
|
|
|
|
#include "vtkCamera.h"
|
|
#include "vtkVolumeRenderingFactory.h"
|
|
#include "vtkImageData.h"
|
|
#include "vtkLargeInteger.h"
|
|
#include "vtkMatrix4x4.h"
|
|
#include "vtkPointData.h"
|
|
#include "vtkRenderWindow.h"
|
|
#include "vtkRenderer.h"
|
|
#include "vtkTransform.h"
|
|
#include "vtkVolumeProperty.h"
|
|
|
|
#define VTK_PLUS_X_MAJOR_DIRECTION 0
|
|
#define VTK_MINUS_X_MAJOR_DIRECTION 1
|
|
#define VTK_PLUS_Y_MAJOR_DIRECTION 2
|
|
#define VTK_MINUS_Y_MAJOR_DIRECTION 3
|
|
#define VTK_PLUS_Z_MAJOR_DIRECTION 4
|
|
#define VTK_MINUS_Z_MAJOR_DIRECTION 5
|
|
|
|
template <class T>
|
|
void vtkVolumeTextureMapper2D_TraverseVolume( T *data_ptr,
|
|
int size[3],
|
|
int axis,
|
|
int directionFlag,
|
|
vtkVolumeTextureMapper2D *me )
|
|
{
|
|
int i, j, k;
|
|
int kstart, kend, kinc;
|
|
unsigned char *tptr;
|
|
T *dptr;
|
|
unsigned short *nptr;
|
|
unsigned char *gptr = NULL;
|
|
float *v, *t;
|
|
unsigned char *rgbaArray = me->GetRGBAArray();
|
|
float *gradientOpacityArray;
|
|
unsigned char *gradientMagnitudes;
|
|
unsigned short *encodedNormals = NULL;
|
|
float *redDiffuseShadingTable = NULL;
|
|
float *greenDiffuseShadingTable = NULL;
|
|
float *blueDiffuseShadingTable = NULL;
|
|
float *redSpecularShadingTable = NULL;
|
|
float *greenSpecularShadingTable = NULL;
|
|
float *blueSpecularShadingTable = NULL;
|
|
int shade;
|
|
float tmpval;
|
|
int cropping, croppingFlags;
|
|
double *croppingBounds;
|
|
int flag[3], tmpFlag, index;
|
|
int clipLow = 0, clipHigh = 0;
|
|
vtkRenderWindow *renWin = me->GetRenderWindow();
|
|
double spacing[3], origin[3];
|
|
unsigned char zero[4];
|
|
unsigned char *texture;
|
|
int textureSize[2];
|
|
int xTile, yTile, xTotal, yTotal, tile, numTiles;
|
|
int *zAxis=0, *yAxis=0, *xAxis=0;
|
|
int loc, inc=0;
|
|
int saveTextures = me->GetSaveTextures();
|
|
int textureOffset=0;
|
|
|
|
int a0=0, a1=0, a2=0;
|
|
|
|
switch ( axis )
|
|
{
|
|
case 0:
|
|
a0 = 1;
|
|
a1 = 2;
|
|
a2 = 0;
|
|
xAxis = &k;
|
|
yAxis = &i;
|
|
zAxis = &j;
|
|
inc = size[0];
|
|
break;
|
|
case 1:
|
|
a0 = 0;
|
|
a1 = 2;
|
|
a2 = 1;
|
|
xAxis = &i;
|
|
yAxis = &k;
|
|
zAxis = &j;
|
|
inc = 1;
|
|
break;
|
|
case 2:
|
|
a0 = 0;
|
|
a1 = 1;
|
|
a2 = 2;
|
|
xAxis = &i;
|
|
yAxis = &j;
|
|
zAxis = &k;
|
|
inc = 1;
|
|
break;
|
|
}
|
|
|
|
int *axisTextureSize = me->GetAxisTextureSize();
|
|
textureSize[0] = axisTextureSize[a2*3+0];
|
|
textureSize[1] = axisTextureSize[a2*3+1];
|
|
|
|
if ( saveTextures )
|
|
{
|
|
texture = me->GetTexture();
|
|
switch ( axis )
|
|
{
|
|
case 0:
|
|
textureOffset = 0;
|
|
break;
|
|
case 1:
|
|
textureOffset =
|
|
4*axisTextureSize[0]*axisTextureSize[1]*axisTextureSize[2];
|
|
break;
|
|
case 2:
|
|
textureOffset =
|
|
4*axisTextureSize[0]*axisTextureSize[1]*axisTextureSize[2] +
|
|
4*axisTextureSize[3]*axisTextureSize[4]*axisTextureSize[5];
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Create space for the texture
|
|
texture = new unsigned char[4*textureSize[0]*textureSize[1]];
|
|
textureOffset = 0;
|
|
}
|
|
|
|
// How many tiles are there in X? in Y? total?
|
|
xTotal = textureSize[0] / size[a0];
|
|
yTotal = textureSize[1] / size[a1];
|
|
numTiles = xTotal * yTotal;
|
|
|
|
// Create space for the vertices and texture coordinates. You need four vertices
|
|
// with three components each for each tile, and four texture coordinates with
|
|
// three components each for each texture coordinate
|
|
v = new float [12*numTiles];
|
|
t = new float [ 8*numTiles];
|
|
|
|
// Convenient for filling in the empty regions (due to clipping)
|
|
zero[0] = 0;
|
|
zero[1] = 0;
|
|
zero[2] = 0;
|
|
zero[3] = 0;
|
|
|
|
// We need to know the spacing and origin of the data to set up the coordinates
|
|
// correctly
|
|
me->GetDataSpacing( spacing );
|
|
me->GetDataOrigin( origin );
|
|
|
|
// What is the first plane, the increment to move to the next plane, and the plane
|
|
// that is just past the end?
|
|
if ( directionFlag )
|
|
{
|
|
kstart = 0;
|
|
kend = ((int)( (size[a2]-1) /
|
|
me->GetInternalSkipFactor())+1)*me->GetInternalSkipFactor();
|
|
|
|
// Offset the slices so that if we take just one it is in the middle
|
|
kstart += (size[a2]-1-kend+me->GetInternalSkipFactor())/2;
|
|
kend += (size[a2]-1-kend+me->GetInternalSkipFactor())/2;
|
|
|
|
kinc = me->GetInternalSkipFactor();
|
|
}
|
|
else
|
|
{
|
|
kstart = (int)((size[a2]-1) /
|
|
me->GetInternalSkipFactor()) * me->GetInternalSkipFactor();
|
|
kend = -me->GetInternalSkipFactor();
|
|
|
|
// Offset the slices so that if we take just one it is in the middle
|
|
kend += (size[a2]-1-kstart)/2;
|
|
kstart += (size[a2]-1-kstart)/2;
|
|
|
|
kinc = -me->GetInternalSkipFactor();
|
|
}
|
|
|
|
// Fill in the texture coordinates and most of the vertex information in advance
|
|
float offset[2];
|
|
offset[0] = 0.5 / (float)textureSize[0];
|
|
offset[1] = 0.5 / (float)textureSize[1];
|
|
|
|
for ( i = 0; i < numTiles; i++ )
|
|
{
|
|
yTile = i / xTotal;
|
|
xTile = i % xTotal;
|
|
|
|
t[i*8 + 0] = (float)((size[a0]*(xTile )) )/(float)textureSize[0] + offset[0];
|
|
t[i*8 + 1] = (float)((size[a1]*(yTile )) )/(float)textureSize[1] + offset[1];
|
|
t[i*8 + 2] = (float)((size[a0]*(xTile )) )/(float)textureSize[0] + offset[0];
|
|
t[i*8 + 3] = (float)((size[a1]*(yTile+1)) )/(float)textureSize[1] - offset[1];
|
|
t[i*8 + 4] = (float)((size[a0]*(xTile+1)) )/(float)textureSize[0] - offset[0];
|
|
t[i*8 + 5] = (float)((size[a1]*(yTile+1)) )/(float)textureSize[1] - offset[1];
|
|
t[i*8 + 6] = (float)((size[a0]*(xTile+1)) )/(float)textureSize[0] - offset[0];
|
|
t[i*8 + 7] = (float)((size[a1]*(yTile )) )/(float)textureSize[1] + offset[1];
|
|
|
|
v[i*12 + a0] = origin[a0];
|
|
v[i*12 + a1] = origin[a1];
|
|
|
|
v[i*12 + 3+a0] = origin[a0];
|
|
v[i*12 + 3+a1] = spacing[a1] * (float)(size[a1]-1) + origin[a1];
|
|
|
|
v[i*12 + 6+a0] = spacing[a0] * (float)(size[a0]-1) + origin[a0];
|
|
v[i*12 + 6+a1] = spacing[a1] * (float)(size[a1]-1) + origin[a1];
|
|
|
|
v[i*12 + 9+a0] = spacing[a0] * (float)(size[a0]-1) + origin[a0];
|
|
v[i*12 + 9+a1] = origin[a1];
|
|
}
|
|
|
|
cropping = me->GetCropping();
|
|
croppingFlags = me->GetCroppingRegionFlags();
|
|
croppingBounds = me->GetVoxelCroppingRegionPlanes();
|
|
|
|
if ( !cropping )
|
|
{
|
|
clipLow = 0;
|
|
clipHigh = size[a0];
|
|
flag[0] = 1;
|
|
flag[1] = 1;
|
|
flag[2] = 1;
|
|
}
|
|
|
|
shade = me->GetShade();
|
|
if ( shade )
|
|
{
|
|
encodedNormals = me->GetEncodedNormals();
|
|
|
|
redDiffuseShadingTable = me->GetRedDiffuseShadingTable();
|
|
greenDiffuseShadingTable = me->GetGreenDiffuseShadingTable();
|
|
blueDiffuseShadingTable = me->GetBlueDiffuseShadingTable();
|
|
|
|
redSpecularShadingTable = me->GetRedSpecularShadingTable();
|
|
greenSpecularShadingTable = me->GetGreenSpecularShadingTable();
|
|
blueSpecularShadingTable = me->GetBlueSpecularShadingTable();
|
|
}
|
|
|
|
gradientMagnitudes = me->GetGradientMagnitudes();
|
|
gradientOpacityArray = me->GetGradientOpacityArray();
|
|
|
|
tile = 0;
|
|
for ( k = kstart; k != kend; k+=kinc )
|
|
{
|
|
yTile = tile / xTotal;
|
|
xTile = tile % xTotal;
|
|
|
|
for ( j = 0; j < size[a1]; j++ )
|
|
{
|
|
i = 0;
|
|
|
|
tptr = texture + textureOffset +
|
|
4 * ( yTile*size[a1]*textureSize[0]+
|
|
j*textureSize[0] +
|
|
xTile*size[a0] );
|
|
|
|
loc = (*zAxis)*size[0]*size[1] + (*yAxis)*size[0] + (*xAxis);
|
|
dptr = data_ptr + loc;
|
|
|
|
// Given a Y and Z value, what are the cropping bounds
|
|
// on X.
|
|
if ( cropping )
|
|
{
|
|
switch ( axis )
|
|
{
|
|
case 0:
|
|
clipLow = (int) croppingBounds[2];
|
|
clipHigh = (int) croppingBounds[3];
|
|
tmpFlag = (*xAxis<croppingBounds[0])?(0):(1+(*xAxis>=croppingBounds[1]));
|
|
tmpFlag+= 9*((*zAxis<croppingBounds[4])?(0):(1+(*zAxis>=croppingBounds[5])));
|
|
flag[0] = croppingFlags&(1<<(tmpFlag));
|
|
flag[1] = croppingFlags&(1<<(tmpFlag+3));
|
|
flag[2] = croppingFlags&(1<<(tmpFlag+6));
|
|
break;
|
|
case 1:
|
|
clipLow = (int)croppingBounds[0];
|
|
clipHigh = (int)croppingBounds[1];
|
|
tmpFlag = 3*((*yAxis<croppingBounds[2])?(0):(1+(*yAxis>=croppingBounds[3])));
|
|
tmpFlag+= 9*((*zAxis<croppingBounds[4])?(0):(1+(*zAxis>=croppingBounds[5])));
|
|
flag[0] = croppingFlags&(1<<(tmpFlag));
|
|
flag[1] = croppingFlags&(1<<(tmpFlag+1));
|
|
flag[2] = croppingFlags&(1<<(tmpFlag+2));
|
|
break;
|
|
case 2:
|
|
clipLow = (int)croppingBounds[0];
|
|
clipHigh = (int)croppingBounds[1];
|
|
tmpFlag = 3*((*yAxis<croppingBounds[2])?(0):(1+(*yAxis>=croppingBounds[3])));
|
|
tmpFlag+= 9*((*zAxis<croppingBounds[4])?(0):(1+(*zAxis>=croppingBounds[5])));
|
|
flag[0] = croppingFlags&(1<<(tmpFlag));
|
|
flag[1] = croppingFlags&(1<<(tmpFlag+1));
|
|
flag[2] = croppingFlags&(1<<(tmpFlag+2));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( shade )
|
|
{
|
|
nptr = encodedNormals + loc;
|
|
|
|
if ( gradientMagnitudes )
|
|
{
|
|
gptr = gradientMagnitudes + loc;
|
|
}
|
|
for ( i = 0; i < size[a0]; i++ )
|
|
{
|
|
index = 0;
|
|
index += ( i >= clipLow );
|
|
index += ( i >= clipHigh );
|
|
if ( flag[index] )
|
|
{
|
|
tmpval = rgbaArray[(*dptr)*4];
|
|
tmpval = tmpval * redDiffuseShadingTable[*nptr] +
|
|
redSpecularShadingTable[*nptr]*255.0;
|
|
if ( tmpval > 255.0 )
|
|
{
|
|
tmpval = 255.0;
|
|
}
|
|
*(tptr++) = (unsigned char) tmpval;
|
|
|
|
tmpval = rgbaArray[(*dptr)*4 + 1];
|
|
tmpval = tmpval * greenDiffuseShadingTable[*nptr] +
|
|
greenSpecularShadingTable[*nptr]*255.0;
|
|
if ( tmpval > 255.0 )
|
|
{
|
|
tmpval = 255.0;
|
|
}
|
|
*(tptr++) = (unsigned char) tmpval;
|
|
|
|
tmpval = rgbaArray[(*dptr)*4 + 2];
|
|
tmpval = tmpval * blueDiffuseShadingTable[*nptr] +
|
|
blueSpecularShadingTable[*nptr]*255.0;
|
|
if ( tmpval > 255.0 )
|
|
{
|
|
tmpval = 255.0;
|
|
}
|
|
*(tptr++) = (unsigned char) tmpval;
|
|
|
|
tmpval = rgbaArray[(*dptr)*4 + 3];
|
|
if ( gradientMagnitudes )
|
|
{
|
|
tmpval *= gradientOpacityArray[*gptr];
|
|
gptr += inc;
|
|
}
|
|
*(tptr++) = (unsigned char) tmpval;
|
|
|
|
}
|
|
else
|
|
{
|
|
memcpy( tptr, zero, 4 );
|
|
tptr += 4;
|
|
if ( gradientMagnitudes )
|
|
{
|
|
gptr += inc;
|
|
}
|
|
}
|
|
nptr += inc;
|
|
dptr += inc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( gradientMagnitudes )
|
|
{
|
|
gptr = gradientMagnitudes + loc;
|
|
}
|
|
|
|
if ( cropping )
|
|
{
|
|
for ( i = 0; i < size[a0]; i++ )
|
|
{
|
|
index = 0;
|
|
index += ( i >= clipLow );
|
|
index += ( i >= clipHigh );
|
|
if ( flag[index] )
|
|
{
|
|
memcpy( tptr, rgbaArray + (*dptr)*4, 4 );
|
|
if ( gradientMagnitudes )
|
|
{
|
|
*(tptr+3) = (unsigned char)
|
|
((float)(*(tptr+3)) * gradientOpacityArray[*gptr]);
|
|
gptr += inc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memcpy( tptr, zero, 4 );
|
|
if ( gradientMagnitudes )
|
|
{
|
|
gptr += inc;
|
|
}
|
|
}
|
|
tptr += 4;
|
|
dptr += inc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( gradientMagnitudes )
|
|
{
|
|
for ( i = 0; i < size[a0]; i++ )
|
|
{
|
|
memcpy( tptr, rgbaArray + (*dptr)*4, 4 );
|
|
*(tptr+3) = (unsigned char)
|
|
((float)(*(tptr+3)) * gradientOpacityArray[*gptr]);
|
|
gptr += inc;
|
|
dptr += inc;
|
|
tptr += 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( i = 0; i < size[a0]; i++ )
|
|
{
|
|
memcpy( tptr, rgbaArray + (*dptr)*4, 4 );
|
|
tptr += 4;
|
|
dptr += inc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( renWin->CheckAbortStatus() )
|
|
{
|
|
break;
|
|
}
|
|
|
|
v[12*tile + a2] =
|
|
v[12*tile + 3+a2] =
|
|
v[12*tile + 6+a2] =
|
|
v[12*tile + 9+a2] = spacing[a2] * (float)k + origin[a2];
|
|
|
|
tile++;
|
|
|
|
if ( tile == numTiles || (k+kinc == kend) )
|
|
{
|
|
if ( saveTextures )
|
|
{
|
|
textureOffset += 4*axisTextureSize[a2*3] * axisTextureSize[a2*3+1];
|
|
}
|
|
else
|
|
{
|
|
me->RenderQuads( tile, v, t, texture, textureSize, 0);
|
|
}
|
|
tile = 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if ( !saveTextures )
|
|
{
|
|
delete [] texture;
|
|
}
|
|
|
|
delete [] v;
|
|
delete [] t;
|
|
|
|
}
|
|
|
|
vtkCxxRevisionMacro(vtkVolumeTextureMapper2D, "$Revision: 1.2 $");
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Needed when we don't use the vtkStandardNewMacro.
|
|
vtkInstantiatorNewMacro(vtkVolumeTextureMapper2D);
|
|
//----------------------------------------------------------------------------
|
|
|
|
vtkVolumeTextureMapper2D::vtkVolumeTextureMapper2D()
|
|
{
|
|
this->TargetTextureSize[0] = 512;
|
|
this->TargetTextureSize[1] = 512;
|
|
this->MaximumNumberOfPlanes = 0;
|
|
this->MaximumStorageSize = 0;
|
|
this->Texture = NULL;
|
|
this->TextureSize = 0;
|
|
}
|
|
|
|
vtkVolumeTextureMapper2D::~vtkVolumeTextureMapper2D()
|
|
{
|
|
if ( this->Texture )
|
|
{
|
|
delete [] this->Texture;
|
|
}
|
|
}
|
|
|
|
|
|
vtkVolumeTextureMapper2D *vtkVolumeTextureMapper2D::New()
|
|
{
|
|
// First try to create the object from the vtkObjectFactory
|
|
vtkObject* ret =
|
|
vtkVolumeRenderingFactory::CreateInstance("vtkVolumeTextureMapper2D");
|
|
return (vtkVolumeTextureMapper2D*)ret;
|
|
}
|
|
|
|
void vtkVolumeTextureMapper2D::RenderSavedTexture()
|
|
{
|
|
int i, k;
|
|
int kstart, kend, kinc;
|
|
unsigned char *tptr;
|
|
float *v, *t;
|
|
vtkRenderWindow *renWin = this->GetRenderWindow();
|
|
double spacing[3], origin[3];
|
|
unsigned char *texture;
|
|
int textureSize[2];
|
|
int xTile, yTile, xTotal, yTotal, tile, numTiles;
|
|
int textureOffset=0;
|
|
int axis=0, directionFlag=0;
|
|
int size[3];
|
|
|
|
int a0=0, a1=0, a2=0;
|
|
|
|
this->GetInput()->GetDimensions( size );
|
|
|
|
switch ( this->MajorDirection )
|
|
{
|
|
case VTK_PLUS_X_MAJOR_DIRECTION:
|
|
axis = 0;
|
|
directionFlag = 1;
|
|
break;
|
|
case VTK_MINUS_X_MAJOR_DIRECTION:
|
|
axis = 0;
|
|
directionFlag = 0;
|
|
break;
|
|
case VTK_PLUS_Y_MAJOR_DIRECTION:
|
|
axis = 1;
|
|
directionFlag = 1;
|
|
break;
|
|
case VTK_MINUS_Y_MAJOR_DIRECTION:
|
|
axis = 1;
|
|
directionFlag = 0;
|
|
break;
|
|
case VTK_PLUS_Z_MAJOR_DIRECTION:
|
|
axis = 2;
|
|
directionFlag = 1;
|
|
break;
|
|
case VTK_MINUS_Z_MAJOR_DIRECTION:
|
|
axis = 2;
|
|
directionFlag = 0;
|
|
break;
|
|
}
|
|
|
|
switch ( axis )
|
|
{
|
|
case 0:
|
|
a0 = 1;
|
|
a1 = 2;
|
|
a2 = 0;
|
|
break;
|
|
case 1:
|
|
a0 = 0;
|
|
a1 = 2;
|
|
a2 = 1;
|
|
break;
|
|
case 2:
|
|
a0 = 0;
|
|
a1 = 1;
|
|
a2 = 2;
|
|
break;
|
|
}
|
|
|
|
textureSize[0] = this->AxisTextureSize[a2][0];
|
|
textureSize[1] = this->AxisTextureSize[a2][1];
|
|
|
|
texture = this->Texture;
|
|
switch ( axis )
|
|
{
|
|
case 0:
|
|
textureOffset = 0;
|
|
break;
|
|
case 1:
|
|
textureOffset =
|
|
4*(this->AxisTextureSize[0][0]*
|
|
this->AxisTextureSize[0][1]*
|
|
this->AxisTextureSize[0][2]);
|
|
break;
|
|
case 2:
|
|
textureOffset =
|
|
4*(this->AxisTextureSize[0][0]*
|
|
this->AxisTextureSize[0][1]*
|
|
this->AxisTextureSize[0][2]) +
|
|
4*(this->AxisTextureSize[1][0]*
|
|
this->AxisTextureSize[1][1]*
|
|
this->AxisTextureSize[1][2]);
|
|
break;
|
|
}
|
|
|
|
if ( directionFlag == 0 )
|
|
{
|
|
textureOffset +=
|
|
4*(this->AxisTextureSize[a2][0]*
|
|
this->AxisTextureSize[a2][1]*
|
|
(this->AxisTextureSize[a2][2]-1));
|
|
}
|
|
|
|
// How many tiles are there in X? in Y? total?
|
|
xTotal = textureSize[0] / size[a0];
|
|
yTotal = textureSize[1] / size[a1];
|
|
numTiles = xTotal * yTotal;
|
|
|
|
// Create space for the vertices and texture coordinates. You need four vertices
|
|
// with three components each for each tile, and four texture coordinates with
|
|
// three components each for each texture coordinate
|
|
v = new float [12*numTiles];
|
|
t = new float [ 8*numTiles];
|
|
|
|
// We need to know the spacing and origin of the data to set up the coordinates
|
|
// correctly
|
|
this->GetDataSpacing( spacing );
|
|
this->GetDataOrigin( origin );
|
|
|
|
// What is the first plane, the increment to move to the next plane, and the plane
|
|
// that is just past the end?
|
|
if ( directionFlag )
|
|
{
|
|
kstart = 0;
|
|
kend = ((int)( (size[a2]-1) /
|
|
this->InternalSkipFactor)+1)*this->InternalSkipFactor;
|
|
|
|
// Offset the slices so that if we take just one it is in the middle
|
|
kstart += (size[a2]-1-kend+this->InternalSkipFactor)/2;
|
|
kend += (size[a2]-1-kend+this->InternalSkipFactor)/2;
|
|
|
|
kinc = this->InternalSkipFactor;
|
|
}
|
|
else
|
|
{
|
|
kstart = (int)((size[a2]-1) /
|
|
this->InternalSkipFactor) * this->InternalSkipFactor;
|
|
kend = -this->InternalSkipFactor;
|
|
|
|
// Offset the slices so that if we take just one it is in the middle
|
|
kend += (size[a2]-1-kstart)/2;
|
|
kstart += (size[a2]-1-kstart)/2;
|
|
|
|
kinc = -this->InternalSkipFactor;
|
|
}
|
|
|
|
// Fill in the texture coordinates and most of the vertex information in advance
|
|
float offset[2];
|
|
offset[0] = 0.5 / (float)textureSize[0];
|
|
offset[1] = 0.5 / (float)textureSize[1];
|
|
|
|
int idx;
|
|
for ( idx = 0; idx < numTiles; idx++ )
|
|
{
|
|
i = ( directionFlag == 1 )?(idx):(numTiles-idx-1);
|
|
|
|
yTile = i / xTotal;
|
|
xTile = i % xTotal;
|
|
|
|
t[i*8 + 0] = (float)((size[a0]*(xTile )) )/(float)textureSize[0] + offset[0];
|
|
t[i*8 + 1] = (float)((size[a1]*(yTile )) )/(float)textureSize[1] + offset[1];
|
|
t[i*8 + 2] = (float)((size[a0]*(xTile )) )/(float)textureSize[0] + offset[0];
|
|
t[i*8 + 3] = (float)((size[a1]*(yTile+1)) )/(float)textureSize[1] - offset[1];
|
|
t[i*8 + 4] = (float)((size[a0]*(xTile+1)) )/(float)textureSize[0] - offset[0];
|
|
t[i*8 + 5] = (float)((size[a1]*(yTile+1)) )/(float)textureSize[1] - offset[1];
|
|
t[i*8 + 6] = (float)((size[a0]*(xTile+1)) )/(float)textureSize[0] - offset[0];
|
|
t[i*8 + 7] = (float)((size[a1]*(yTile )) )/(float)textureSize[1] + offset[1];
|
|
|
|
v[i*12 + a0] = origin[a0];
|
|
v[i*12 + a1] = origin[a1];
|
|
|
|
v[i*12 + 3+a0] = origin[a0];
|
|
v[i*12 + 3+a1] = spacing[a1] * (float)(size[a1]-1) + origin[a1];
|
|
|
|
v[i*12 + 6+a0] = spacing[a0] * (float)(size[a0]-1) + origin[a0];
|
|
v[i*12 + 6+a1] = spacing[a1] * (float)(size[a1]-1) + origin[a1];
|
|
|
|
v[i*12 + 9+a0] = spacing[a0] * (float)(size[a0]-1) + origin[a0];
|
|
v[i*12 + 9+a1] = origin[a1];
|
|
}
|
|
|
|
if ( directionFlag == 1 )
|
|
{
|
|
tile = 0;
|
|
}
|
|
else
|
|
{
|
|
tile = (((kend - kstart)/kinc)-1)%numTiles;
|
|
}
|
|
|
|
int tileCount = 0;
|
|
|
|
for ( k = kstart; k != kend; k+=kinc )
|
|
{
|
|
if ( renWin->CheckAbortStatus() )
|
|
{
|
|
break;
|
|
}
|
|
|
|
v[12*tile + a2] =
|
|
v[12*tile + 3+a2] =
|
|
v[12*tile + 6+a2] =
|
|
v[12*tile + 9+a2] = spacing[a2] * (float)k + origin[a2];
|
|
|
|
tileCount++;
|
|
|
|
if ( directionFlag == 1 )
|
|
{
|
|
tile++;
|
|
}
|
|
else
|
|
{
|
|
tile--;
|
|
}
|
|
|
|
if ( (directionFlag == 1 && tile == numTiles ) ||
|
|
(directionFlag == 0 && tile == -1) || (k+kinc == kend) )
|
|
{
|
|
tptr = texture + textureOffset;
|
|
if ( directionFlag == 1 )
|
|
{
|
|
textureOffset +=
|
|
4*this->AxisTextureSize[a2][0] * this->AxisTextureSize[a2][1];
|
|
}
|
|
else
|
|
{
|
|
textureOffset -=
|
|
4*this->AxisTextureSize[a2][0] * this->AxisTextureSize[a2][1];
|
|
}
|
|
|
|
this->RenderQuads( tileCount, v, t, tptr, textureSize, !directionFlag );
|
|
tile = (directionFlag == 1)?(0):(numTiles-1);
|
|
tileCount = 0;
|
|
}
|
|
}
|
|
|
|
delete [] v;
|
|
delete [] t;
|
|
|
|
}
|
|
|
|
void vtkVolumeTextureMapper2D::GenerateTexturesAndRenderQuads( vtkRenderer *ren, vtkVolume *vol )
|
|
{
|
|
vtkImageData *input = this->GetInput();
|
|
int size[3];
|
|
void *inputPointer;
|
|
int inputType;
|
|
|
|
inputPointer =
|
|
input->GetPointData()->GetScalars()->GetVoidPointer(0);
|
|
inputType =
|
|
input->GetPointData()->GetScalars()->GetDataType();
|
|
|
|
input->GetDimensions( size );
|
|
|
|
// Do we have a texture already, and nothing has changed? If so
|
|
// just render it.
|
|
if ( this->Texture && !this->Shade &&
|
|
this->GetMTime() < this->TextureMTime &&
|
|
this->GetInput()->GetMTime() < this->TextureMTime &&
|
|
vol->GetProperty()->GetMTime() < this->TextureMTime )
|
|
{
|
|
this->RenderSavedTexture();
|
|
return;
|
|
}
|
|
|
|
// Otherwise, we need to generate textures. We can throw away any
|
|
// saved textures
|
|
if ( this->Texture )
|
|
{
|
|
delete [] this->Texture;
|
|
this->Texture = NULL;
|
|
}
|
|
this->TextureSize = 0;
|
|
|
|
// Will all the textures fit in the allotted storage?
|
|
this->ComputeAxisTextureSize( 0, this->AxisTextureSize[0] );
|
|
this->ComputeAxisTextureSize( 1, this->AxisTextureSize[1] );
|
|
this->ComputeAxisTextureSize( 2, this->AxisTextureSize[2] );
|
|
|
|
vtkLargeInteger neededSize;
|
|
vtkLargeInteger tmpInt;
|
|
|
|
|
|
neededSize =
|
|
this->AxisTextureSize[0][0];
|
|
neededSize = neededSize *
|
|
this->AxisTextureSize[0][1] *
|
|
this->AxisTextureSize[0][2] ;
|
|
|
|
tmpInt =
|
|
this->AxisTextureSize[1][0];
|
|
tmpInt = tmpInt *
|
|
this->AxisTextureSize[1][1] *
|
|
this->AxisTextureSize[1][2];
|
|
neededSize = neededSize + tmpInt;
|
|
|
|
tmpInt =
|
|
this->AxisTextureSize[2][0];
|
|
tmpInt = tmpInt *
|
|
this->AxisTextureSize[2][1] *
|
|
this->AxisTextureSize[2][2];
|
|
neededSize = neededSize + tmpInt;
|
|
|
|
neededSize *= 4;
|
|
|
|
if ( neededSize.GetLength() > 31 )
|
|
{
|
|
this->SaveTextures = 0;
|
|
}
|
|
else
|
|
{
|
|
this->SaveTextures =
|
|
( neededSize.CastToLong() <= this->MaximumStorageSize &&
|
|
!this->Shade );
|
|
}
|
|
|
|
if ( this->SaveTextures )
|
|
{
|
|
this->Texture = new unsigned char [neededSize.CastToLong()];
|
|
this->TextureSize = neededSize.CastToLong();
|
|
|
|
int savedDirection = this->MajorDirection;
|
|
|
|
switch ( inputType )
|
|
{
|
|
case VTK_UNSIGNED_CHAR:
|
|
this->InitializeRender( ren, vol, VTK_PLUS_X_MAJOR_DIRECTION );
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned char *)inputPointer, size, 0, 1, this );
|
|
|
|
this->InitializeRender( ren, vol, VTK_PLUS_Y_MAJOR_DIRECTION );
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned char *)inputPointer, size, 1, 1, this );
|
|
|
|
this->InitializeRender( ren, vol, VTK_PLUS_Z_MAJOR_DIRECTION );
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned char *)inputPointer, size, 2, 1, this );
|
|
break;
|
|
case VTK_UNSIGNED_SHORT:
|
|
this->InitializeRender( ren, vol, VTK_PLUS_X_MAJOR_DIRECTION );
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned short *)inputPointer, size, 0, 1, this );
|
|
|
|
this->InitializeRender( ren, vol, VTK_PLUS_Y_MAJOR_DIRECTION );
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned short *)inputPointer, size, 1, 1, this );
|
|
|
|
this->InitializeRender( ren, vol, VTK_PLUS_Z_MAJOR_DIRECTION );
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned short *)inputPointer, size, 2, 1, this );
|
|
break;
|
|
}
|
|
|
|
this->MajorDirection = savedDirection;
|
|
if ( !ren->GetRenderWindow()->GetAbortRender() )
|
|
{
|
|
this->RenderSavedTexture();
|
|
this->TextureMTime.Modified();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
switch ( inputType )
|
|
{
|
|
case VTK_UNSIGNED_CHAR:
|
|
switch ( this->MajorDirection )
|
|
{
|
|
case VTK_PLUS_X_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned char *)inputPointer, size, 0, 1, this );
|
|
break;
|
|
|
|
case VTK_MINUS_X_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned char *)inputPointer, size, 0, 0, this );
|
|
break;
|
|
|
|
case VTK_PLUS_Y_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned char *)inputPointer, size, 1, 1, this );
|
|
break;
|
|
|
|
case VTK_MINUS_Y_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned char *)inputPointer, size, 1, 0, this );
|
|
break;
|
|
|
|
case VTK_PLUS_Z_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned char *)inputPointer, size, 2, 1, this );
|
|
break;
|
|
|
|
case VTK_MINUS_Z_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned char *)inputPointer, size, 2, 0, this );
|
|
break;
|
|
}
|
|
break;
|
|
case VTK_UNSIGNED_SHORT:
|
|
switch ( this->MajorDirection )
|
|
{
|
|
case VTK_PLUS_X_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned short *)inputPointer, size, 0, 1, this );
|
|
break;
|
|
|
|
case VTK_MINUS_X_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned short *)inputPointer, size, 0, 0, this );
|
|
break;
|
|
|
|
case VTK_PLUS_Y_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned short *)inputPointer, size, 1, 1, this );
|
|
break;
|
|
|
|
case VTK_MINUS_Y_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned short *)inputPointer, size, 1, 0, this );
|
|
break;
|
|
|
|
case VTK_PLUS_Z_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned short *)inputPointer, size, 2, 1, this );
|
|
break;
|
|
|
|
case VTK_MINUS_Z_MAJOR_DIRECTION:
|
|
vtkVolumeTextureMapper2D_TraverseVolume
|
|
( (unsigned short *)inputPointer, size, 2, 0, this );
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
vtkErrorMacro(
|
|
"vtkVolumeTextureMapper2D only works with unsigned short and unsigned char data.\n" <<
|
|
"Input type: " << inputType << " given.");
|
|
}
|
|
}
|
|
}
|
|
|
|
void vtkVolumeTextureMapper2D::InitializeRender( vtkRenderer *ren,
|
|
vtkVolume *vol,
|
|
int majorDirection )
|
|
{
|
|
if ( majorDirection >= 0)
|
|
{
|
|
this->MajorDirection = majorDirection;
|
|
}
|
|
else
|
|
{
|
|
double vpn[3];
|
|
|
|
// Take the vpn, convert it to volume coordinates, and find the
|
|
// major direction
|
|
vtkMatrix4x4 *volMatrix = vtkMatrix4x4::New();
|
|
volMatrix->DeepCopy( vol->GetMatrix() );
|
|
vtkTransform *worldToVolumeTransform = vtkTransform::New();
|
|
worldToVolumeTransform->SetMatrix( volMatrix );
|
|
|
|
// Create a transform that will account for the translation of
|
|
// the scalar data.
|
|
vtkTransform *volumeTransform = vtkTransform::New();
|
|
|
|
volumeTransform->Identity();
|
|
volumeTransform->Translate(this->GetInput()->GetOrigin());
|
|
|
|
// Now concatenate the volume's matrix with this scalar data matrix
|
|
worldToVolumeTransform->PreMultiply();
|
|
worldToVolumeTransform->Concatenate( volumeTransform->GetMatrix() );
|
|
worldToVolumeTransform->Inverse();
|
|
|
|
ren->GetActiveCamera()->GetViewPlaneNormal(vpn);
|
|
worldToVolumeTransform->TransformVector( vpn, vpn );
|
|
|
|
volMatrix->Delete();
|
|
volumeTransform->Delete();
|
|
worldToVolumeTransform->Delete();
|
|
|
|
if ( fabs(vpn[0]) >= fabs(vpn[1]) && fabs(vpn[0]) >= fabs(vpn[2]) )
|
|
{
|
|
this->MajorDirection = (vpn[0]<0.0)?
|
|
(VTK_MINUS_X_MAJOR_DIRECTION):(VTK_PLUS_X_MAJOR_DIRECTION);
|
|
}
|
|
else if ( fabs(vpn[1]) >= fabs(vpn[0]) && fabs(vpn[1]) >= fabs(vpn[2]) )
|
|
{
|
|
this->MajorDirection = (vpn[1]<0.0)?
|
|
(VTK_MINUS_Y_MAJOR_DIRECTION):(VTK_PLUS_Y_MAJOR_DIRECTION);
|
|
}
|
|
else
|
|
{
|
|
this->MajorDirection = (vpn[2]<0.0)?
|
|
(VTK_MINUS_Z_MAJOR_DIRECTION):(VTK_PLUS_Z_MAJOR_DIRECTION);
|
|
}
|
|
}
|
|
|
|
// Determine the internal skip factor - if there is a limit on the number
|
|
// of planes we can have (the MaximumNumberOfPlanes value is greater than
|
|
// 0) then increase this skip factor until we ensure the maximum condition.
|
|
this->InternalSkipFactor = 1;
|
|
if ( this->MaximumNumberOfPlanes > 0 )
|
|
{
|
|
int size[3];
|
|
this->GetInput()->GetDimensions( size );
|
|
while ( (float)size[this->MajorDirection/2] /
|
|
(float)this->InternalSkipFactor >
|
|
(float)this->MaximumNumberOfPlanes )
|
|
{
|
|
this->InternalSkipFactor++;
|
|
}
|
|
}
|
|
// Assume that the spacing between samples is 1/2 of the maximum - this
|
|
// could be computed accurately for parallel (but isn't right now). For
|
|
// perspective, this spacing changes across the image so no one number will
|
|
// be accurate. 1/2 the maximum is (1 + sqrt(2)) / 2 = 1.2071
|
|
|
|
// TODO: DataSpacing should be converted to double at some point
|
|
double *dspacing;
|
|
dspacing = this->GetInput()->GetSpacing();
|
|
this->DataSpacing[0] = (float)dspacing[0];
|
|
this->DataSpacing[1] = (float)dspacing[1];
|
|
this->DataSpacing[2] = (float)dspacing[2];
|
|
this->SampleDistance =
|
|
this->DataSpacing[this->MajorDirection/2]*this->InternalSkipFactor*1.2071;
|
|
this->vtkVolumeTextureMapper::InitializeRender( ren, vol );
|
|
}
|
|
|
|
void vtkVolumeTextureMapper2D::ComputeAxisTextureSize( int axis, int *textureSize )
|
|
{
|
|
int targetSize[2];
|
|
int a0=0, a1=0, a2=0;
|
|
|
|
switch ( axis )
|
|
{
|
|
case 0:
|
|
a0 = 1;
|
|
a1 = 2;
|
|
a2 = 0;
|
|
break;
|
|
case 1:
|
|
a0 = 0;
|
|
a1 = 2;
|
|
a2 = 1;
|
|
break;
|
|
case 2:
|
|
a0 = 0;
|
|
a1 = 1;
|
|
a2 = 2;
|
|
break;
|
|
}
|
|
|
|
|
|
// How big should the texture be?
|
|
// Start with the target size
|
|
targetSize[0] = this->TargetTextureSize[0];
|
|
targetSize[1] = this->TargetTextureSize[1];
|
|
|
|
int size[3];
|
|
this->GetInput()->GetDimensions( size );
|
|
|
|
// Increase the x dimension of the texture if the x dimension of the data
|
|
// is bigger than it (because these are x by y textures)
|
|
if ( size[a0] > targetSize[0] )
|
|
{
|
|
targetSize[0] = size[a0];
|
|
}
|
|
|
|
// Increase the y dimension of the texture if the y dimension of the data
|
|
// is bigger than it (because these are x by y textures)
|
|
if ( size[a1] > targetSize[1] )
|
|
{
|
|
targetSize[1] = size[a1];
|
|
}
|
|
|
|
// Make sure the x dimension of the texture is a power of 2
|
|
textureSize[0] = 32;
|
|
while( textureSize[0] < targetSize[0] )
|
|
{
|
|
textureSize[0] *= 2;
|
|
}
|
|
|
|
// Make sure the y dimension of the texture is a power of 2
|
|
textureSize[1] = 32;
|
|
while( textureSize[1] < targetSize[1] )
|
|
{
|
|
textureSize[1] *= 2;
|
|
}
|
|
|
|
// Our texture might be too big - shrink it carefully making
|
|
// sure that it is still big enough in the right dimensions to
|
|
// handle oddly shaped volumes
|
|
int volSize = size[0]*size[1]*size[2];
|
|
int done = (volSize > textureSize[0]*textureSize[1]);
|
|
int minSize[2];
|
|
|
|
// What is the minumum size the texture could be in X (along the X
|
|
// axis of the volume)?
|
|
minSize[0] = 32;
|
|
while ( minSize[0] < size[a0] )
|
|
{
|
|
minSize[0] *= 2;
|
|
}
|
|
|
|
// What is the minumum size the texture could be in Y (along the Y
|
|
// axis of the volume)?
|
|
minSize[1] = 32;
|
|
while ( minSize[1] < size[a1] )
|
|
{
|
|
minSize[1] *= 2;
|
|
}
|
|
|
|
// Keep reducing the texture size until it is just big enough
|
|
while (!done)
|
|
{
|
|
// Set done to 1. Reset to 0 if we make any changes.
|
|
done = 1;
|
|
|
|
// If the texture is bigger in some dimension that it needs to be
|
|
// and chopping that dimension in half would still fit the whole
|
|
// volume, then chop it in half.
|
|
if ( textureSize[0] > minSize[0] &&
|
|
( ((textureSize[0]/2) / size[a0]) *
|
|
(textureSize[1] / size[a1]) >= size[a2] ) )
|
|
{
|
|
textureSize[0] /= 2;
|
|
done = 0;
|
|
}
|
|
if ( textureSize[1] > minSize[1] &&
|
|
( (textureSize[0] / size[a0]) *
|
|
((textureSize[1]/2) / size[a1]) >= size[a2] ) )
|
|
{
|
|
textureSize[1] /= 2;
|
|
done = 0;
|
|
}
|
|
}
|
|
|
|
// This is how many texture planes would be necessary if one slice fit on a
|
|
// texture (taking into account the user defined maximum)
|
|
textureSize[2] =
|
|
(size[a2]<this->MaximumNumberOfPlanes||this->MaximumNumberOfPlanes<=0) ?
|
|
(size[a2]) : (this->MaximumNumberOfPlanes);
|
|
|
|
// How many slices can fit on a texture in X and Y?
|
|
int xTotal = textureSize[0] / size[a0];
|
|
int yTotal = textureSize[1] / size[a1];
|
|
|
|
// The number of textures we need is the number computed above divided by
|
|
// how many fit on a texture (plus one if they don't fit evenly)
|
|
textureSize[2] = (textureSize[2] / (xTotal*yTotal)) +
|
|
((textureSize[2] % (xTotal*yTotal))!=0);
|
|
}
|
|
|
|
|
|
// Print the vtkVolumeTextureMapper2D
|
|
void vtkVolumeTextureMapper2D::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
os << indent << "Target Texture Size: "
|
|
<< this->TargetTextureSize[0] << ", "
|
|
<< this->TargetTextureSize[1] << endl;
|
|
|
|
os << indent << "Maximum Number Of Planes: ";
|
|
if ( this->MaximumNumberOfPlanes > 0 )
|
|
{
|
|
os << this->MaximumNumberOfPlanes << endl;
|
|
}
|
|
else
|
|
{
|
|
os << "<unlimited>" << endl;
|
|
}
|
|
|
|
os << indent << "Maximum Storage Size: "
|
|
<< this->MaximumStorageSize << endl;
|
|
|
|
this->Superclass::PrintSelf(os,indent);
|
|
}
|
|
|
|
|
|
|