diff --git a/Core/Code/Controllers/mitkCameraController.cpp b/Core/Code/Controllers/mitkCameraController.cpp index c9aa71c183..841e583321 100644 --- a/Core/Code/Controllers/mitkCameraController.cpp +++ b/Core/Code/Controllers/mitkCameraController.cpp @@ -1,165 +1,168 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html 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 notices for more information. =========================================================================*/ #include "mitkCameraController.h" #include "mitkVtkPropRenderer.h" #include "mitkRenderingManager.h" #include #include "vtkCommand.h" #include "vtkCamera.h" #include "vtkRenderer.h" mitk::CameraController::CameraController(const char * type) : BaseController(type), m_Renderer(NULL), m_ZoomFactor(1.0) {} mitk::CameraController::~CameraController() {} void mitk::CameraController::Resize(int, int) {} void mitk::CameraController::MousePressEvent(mitk::MouseEvent*) {} void mitk::CameraController::MouseReleaseEvent(mitk::MouseEvent*) {} void mitk::CameraController::MouseMoveEvent(mitk::MouseEvent*) {} void mitk::CameraController::KeyPressEvent(mitk::KeyEvent*) {} void mitk::CameraController::SetViewToAnterior() { this->SetStandardView(ANTERIOR); } void mitk::CameraController::SetViewToPosterior() { this->SetStandardView(POSTERIOR); } void mitk::CameraController::SetViewToSinister() { this->SetStandardView(SINISTER); } void mitk::CameraController::SetViewToDexter() { this->SetStandardView(DEXTER); } void mitk::CameraController::SetViewToCranial() { this->SetStandardView(CRANIAL); } void mitk::CameraController::SetViewToCaudal() { this->SetStandardView(CAUDAL); } void mitk::CameraController::SetStandardView( mitk::CameraController::StandardView view ) { const mitk::VtkPropRenderer* glRenderer = dynamic_cast(m_Renderer); if (glRenderer == NULL) return; vtkRenderer* vtkRenderer = glRenderer->GetVtkRenderer(); assert (vtkRenderer); mitk::BoundingBox::Pointer bb; mitk::DataStorage* ds = m_Renderer->GetDataStorage(); if (ds != NULL) bb = ds->ComputeBoundingBox(); else return; mitk::Point3D middle = bb->GetCenter(); - vtkRenderer->GetActiveCamera()->SetFocalPoint(middle[0], middle[1], middle[2]); +// vtkRenderer->GetActiveCamera()->SetFocalPoint(middle[0], middle[1], middle[2]); switch(view) { case ANTERIOR: case POSTERIOR: case SINISTER: case DEXTER: - vtkRenderer->GetActiveCamera()->SetViewUp(0,0,1); +// vtkRenderer->GetActiveCamera()->SetViewUp(0,0,1); break; case CRANIAL: case CAUDAL: - vtkRenderer->GetActiveCamera()->SetViewUp(0,-1,0); +// vtkRenderer->GetActiveCamera()->SetViewUp(0,-1,0); + break; } switch(view) { case ANTERIOR: - vtkRenderer->GetActiveCamera()->SetPosition(middle[0],-100000,middle[2]); +// vtkRenderer->GetActiveCamera()->SetPosition(middle[0],-100000,middle[2]); break; case POSTERIOR: - vtkRenderer->GetActiveCamera()->SetPosition(middle[0],+100000,middle[2]); +// vtkRenderer->GetActiveCamera()->SetPosition(middle[0],+100000,middle[2]); break; case SINISTER: - vtkRenderer->GetActiveCamera()->SetPosition(+100000,middle[1],middle[2]); +// vtkRenderer->GetActiveCamera()->SetPosition(+100000,middle[1],middle[2]); break; case DEXTER: - vtkRenderer->GetActiveCamera()->SetPosition(-100000,middle[1],middle[2]); +// vtkRenderer->GetActiveCamera()->SetPosition(-100000,middle[1],middle[2]); break; case CRANIAL: - vtkRenderer->GetActiveCamera()->SetPosition(middle[0],middle[1],100000); +// vtkRenderer->GetActiveCamera()->SetPosition(middle[0],middle[1],100000); break; case CAUDAL: - vtkRenderer->GetActiveCamera()->SetPosition(middle[0],middle[1],-100000); +// vtkRenderer->GetActiveCamera()->SetPosition(middle[0],middle[1],-100000); break; } - vtkRenderer->ResetCamera(); - double *cameraPosition = vtkRenderer->GetActiveCamera()->GetPosition(); +// vtkRenderer->ResetCamera(); +// double *cameraPosition = vtkRenderer->GetActiveCamera()->GetPosition(); switch(view) { case ANTERIOR: case POSTERIOR: - vtkRenderer->GetActiveCamera()->SetPosition(cameraPosition[0],cameraPosition[1] / m_ZoomFactor,cameraPosition[2]); +// vtkRenderer->GetActiveCamera()->SetPosition(cameraPosition[0],cameraPosition[1] / m_ZoomFactor,cameraPosition[2]); break; case SINISTER: case DEXTER: - vtkRenderer->GetActiveCamera()->SetPosition(cameraPosition[0] / m_ZoomFactor,cameraPosition[1],cameraPosition[2]); +// vtkRenderer->GetActiveCamera()->SetPosition(cameraPosition[0] / m_ZoomFactor,cameraPosition[1],cameraPosition[2]); break; case CRANIAL: case CAUDAL: - vtkRenderer->GetActiveCamera()->SetPosition(cameraPosition[0],cameraPosition[1],cameraPosition[2] / m_ZoomFactor); +// vtkRenderer->GetActiveCamera()->SetPosition(cameraPosition[0],cameraPosition[1],cameraPosition[2] / m_ZoomFactor); break; } - vtkRenderer->ResetCameraClippingRange(); +// vtkRenderer->ResetCameraClippingRange(); + MITK_INFO << "view: " << view; + MITK_INFO << "mitkCameraController.cpp"; mitk::RenderingManager* rm = m_Renderer->GetRenderingManager(); rm->RequestUpdateAll(); } diff --git a/Core/Code/Rendering/mitkImageVtkMapper2D.cpp b/Core/Code/Rendering/mitkImageVtkMapper2D.cpp index 35965dbb5c..677781ae75 100644 --- a/Core/Code/Rendering/mitkImageVtkMapper2D.cpp +++ b/Core/Code/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1218 +1,1224 @@ /*========================================================================= Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html 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 notices for more information. =========================================================================*/ //MITK #include #include #include #include #include #include #include #include #include #include #include #include //MITK Rendering #include "mitkImageVtkMapper2D.h" #include "mitkVtkPropRenderer.h" #include "vtkMitkThickSlicesFilter.h" //VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include -#include #include #include #include #include //ITK #include int mitk::ImageVtkMapper2D::numRenderer = 0; //Number of renderers data is stored for. mitk::ImageVtkMapper2D::ImageVtkMapper2D() { m_VtkBased = true; } mitk::ImageVtkMapper2D::~ImageVtkMapper2D() { this->Clear(); this->InvokeEvent( itk::DeleteEvent() ); //TODO <- what is this doing exactly? } void mitk::ImageVtkMapper2D::AdjustCamera(mitk::BaseRenderer* renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //activate parallel projection for 2D renderer->GetVtkRenderer()->GetActiveCamera()->SetParallelProjection(true); const mitk::DisplayGeometry* displayGeometry = renderer->GetDisplayGeometry(); double imageHeightInMM = localStorage->m_ReslicedImage->GetDimensions()[1]; //the height of the current slice in mm double displayHeightInMM = displayGeometry->GetSizeInMM()[1]; //the display height in mm (gets smaller when you zoom in) // double zoomFactor = displayHeightInMM/imageHeightInMM; //determine how much of the image can be displayed double zoomFactor = imageHeightInMM/displayHeightInMM; //determine how much of the image can be displayed Vector2D displayGeometryOriginInMM = displayGeometry->GetOriginInMM(); //top left of the render window (Origin) Vector2D displayGeometryCenterInMM = displayGeometryOriginInMM + displayGeometry->GetSizeInMM()*0.5; //center of the render window: (Origin + Size/2) //Scale the rendered object: //The image is scaled by a single factor, because in an orthographic projection sizes //are preserved (so you cannot scale X and Y axis with different parameters). The //parameter sets the size of the total display-volume. If you set this to the image //height, the image plus a border with the size of the image will be rendered. //Therefore, the size is imageHeightInMM / 2. renderer->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(imageHeightInMM*0.5 ); //zooming with the factor calculated by dividing displayHeight through imegeHeight. The factor is inverse, because the VTK zoom method is working inversely. renderer->GetVtkRenderer()->GetActiveCamera()->Zoom(zoomFactor); //the center of the view-plane double viewPlaneCenter[3]; viewPlaneCenter[0] = displayGeometryCenterInMM[0]; viewPlaneCenter[1] = displayGeometryCenterInMM[1]; viewPlaneCenter[2] = 0.0; //the view-plane is located in the XY-plane with Z=0.0 //define which direction is "up" for the ciamera (like default for vtk (0.0, 1.0, 0.0) double cameraUp[3]; cameraUp[0] = 0.0; cameraUp[1] = 1.0; cameraUp[2] = 0.0; //the position of the camera (center[0], center[1], 1000) double cameraPosition[3]; cameraPosition[0] = viewPlaneCenter[0]; cameraPosition[1] = viewPlaneCenter[1]; - cameraPosition[2] = viewPlaneCenter[2] + 5000.0; //Reason for 5000 => VTK seems to calculate the clipping planes wrong for Z=1 + cameraPosition[2] = 1.0; //Reason for 5000 => VTK seems to calculate the clipping planes wrong for Z=1 //set the camera corresponding to the textured plane vtkSmartPointer camera = renderer->GetVtkRenderer()->GetActiveCamera(); if (camera) { camera->SetPosition( cameraPosition ); //set the camera position on the textured plane normal (in our case this is the view plane normal) camera->SetFocalPoint( viewPlaneCenter ); //set the focal point to the center of the textured plane camera->SetViewUp( cameraUp ); //set the view-up for the camera } //reset the clipping range - renderer->GetVtkRenderer()->ResetCameraClippingRange(); +// renderer->GetVtkRenderer()->ResetCameraClippingRange(); + camera->SetClippingRange(0.5, 100.5); } //set the two points defining the textured plane according to the dimension and spacing void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer* renderer, vtkFloatingPointType planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //Set the origin to (xMin; yMin; 0) of the plane. This is necessary for obtaining the correct //plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], 0.0); //These two points define the axes of the plane in combination with the origin. //Point 1 is the x-axis and point 2 the y-axis. //Each plane is transformed according to the view (transversal, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], 0.0); //P1: (xMax, yMin, 0) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], 0.0); //P2: (xMin, yMax, 0) } const mitk::Image* mitk::ImageVtkMapper2D::GetInput( void ) { return static_cast< const mitk::Image * >( this->GetData() ); } vtkProp* mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer* renderer) { this->Update(renderer); //return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actor; } void mitk::ImageVtkMapper2D::MitkRenderOverlay(BaseRenderer* renderer) { if ( this->IsVisible(renderer)==false ) return; if ( this->GetVtkProp(renderer)->GetVisibility() ) { this->GetVtkProp(renderer)->RenderOverlay(renderer->GetVtkRenderer()); } } void mitk::ImageVtkMapper2D::MitkRenderOpaqueGeometry(BaseRenderer* renderer) { if ( this->IsVisible( renderer )==false ) return; if ( this->GetVtkProp(renderer)->GetVisibility() ) { // vtkSmartPointer ren = // vtkSmartPointer::New(); // vtkSmartPointer renderWindow = // vtkSmartPointer::New(); // renderWindow->AddRenderer(ren); // vtkSmartPointer renderWindowInteractor = // vtkSmartPointer::New(); // renderWindowInteractor->SetRenderWindow(renderWindow); // ren->AddActor(m_LSH.GetLocalStorage(renderer)->m_Actor); // renderWindow->Render(); // renderWindowInteractor->Start(); this->GetVtkProp(renderer)->RenderOpaqueGeometry( renderer->GetVtkRenderer() ); } } void mitk::ImageVtkMapper2D::MitkRenderTranslucentGeometry(BaseRenderer* renderer) { if ( this->IsVisible(renderer)==false ) return; //TODO is it possible to have a visible BaseRenderer AND an invisible VtkRenderer??? if ( this->GetVtkProp(renderer)->GetVisibility() ) { this->GetVtkProp(renderer)->RenderTranslucentPolygonalGeometry(renderer->GetVtkRenderer()); } } void mitk::ImageVtkMapper2D::MitkRenderVolumetricGeometry(BaseRenderer* renderer) { if(IsVisible(renderer)==false) return; //TODO is it possible to have a visible BaseRenderer AND an invisible VtkRenderer??? if ( GetVtkProp(renderer)->GetVisibility() ) this->GetVtkProp(renderer)->RenderVolumetricGeometry(renderer->GetVtkRenderer()); } void mitk::ImageVtkMapper2D::GenerateData( mitk::BaseRenderer *renderer ) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::Image *input = const_cast< mitk::Image * >( this->GetInput() ); //TODO WTF CONST CAST?!?!?111 => Error in class design? if ( input == NULL ) { return; } RendererInfo &rendererInfo = this->AccessRendererInfo( renderer ); //check if there is a valid worldGeometry TODO: Move to Update()? const Geometry2D *worldGeometry = renderer->GetCurrentWorldGeometry2D(); if( ( worldGeometry == NULL ) || ( !worldGeometry->IsValid() ) || ( !worldGeometry->HasReferenceGeometry() )) { return; } // check if there is something to display. TODO: Move to Update()? if ( !input->IsVolumeSet( this->GetTimestep() ) ) return; input->Update(); vtkImageData* inputData = input->GetVtkImageData( this->GetTimestep() ); if ( inputData == NULL ) { return; } // how big the area is in physical coordinates: widthInMM x heightInMM pixels mitk::ScalarType widthInMM, heightInMM; // where we want to sample Point3D origin; Vector3D right, bottom, normal; // take transform of input image into account const TimeSlicedGeometry *inputTimeGeometry = input->GetTimeSlicedGeometry(); const Geometry3D* inputGeometry = inputTimeGeometry->GetGeometry3D( this->GetTimestep() ); //World spacing ScalarType mmPerPixel[2]; // Bounds information for reslicing (only reuqired if reference geometry // is present) vtkFloatingPointType sliceBounds[6]; bool boundsInitialized = false; for ( int i = 0; i < 6; ++i ) { sliceBounds[i] = 0.0; } //Extent (in pixels) of the image Vector2D extent; // Do we have a simple PlaneGeometry? // This is the "regular" case (e.g. slicing through an image axis-parallel or even oblique) const PlaneGeometry *planeGeometry = dynamic_cast< const PlaneGeometry * >( worldGeometry ); if ( planeGeometry != NULL ) { origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector( 0 ); // right = Extent of Image in mm (worldspace) bottom = planeGeometry->GetAxisVector( 1 ); normal = planeGeometry->GetNormal(); bool inPlaneResampleExtentByGeometry = false; GetDataNode()->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); if ( inPlaneResampleExtentByGeometry ) { // Resampling grid corresponds to the current world geometry. This // means that the spacing of the output 2D image depends on the // currently selected world geometry, and *not* on the image itself. extent[0] = worldGeometry->GetExtent( 0 ); extent[1] = worldGeometry->GetExtent( 1 ); } else { // Resampling grid corresponds to the input geometry. This means that // the spacing of the output 2D image is directly derived from the // associated input image, regardless of the currently selected world // geometry. //TODO use new method instead of deprecated Vector3D rightInIndex, bottomInIndex; inputGeometry->WorldToIndex( origin, right, rightInIndex ); inputGeometry->WorldToIndex( origin, bottom, bottomInIndex ); extent[0] = rightInIndex.GetNorm(); extent[1] = bottomInIndex.GetNorm(); } // Get the extent of the current world geometry and calculate resampling // spacing therefrom. widthInMM = worldGeometry->GetExtentInMM( 0 ); heightInMM = worldGeometry->GetExtentInMM( 1 ); mmPerPixel[0] = widthInMM / extent[0]; mmPerPixel[1] = heightInMM / extent[1]; right.Normalize(); bottom.Normalize(); normal.Normalize(); //Translate the origin from center based to corner based //by adding (mm per pixel)/2 in the corresponding direction (right/bottom). origin += right * ( mmPerPixel[0] * 0.5 ); origin += bottom * ( mmPerPixel[1] * 0.5 ); // Use inverse transform of the input geometry for reslicing the 3D image localStorage->m_Reslicer->SetResliceTransform( inputGeometry->GetVtkTransform()->GetLinearInverse() ); // Set background level to TRANSLUCENT (see Geometry2DDataVtkMapper3D) localStorage->m_Reslicer->SetBackgroundLevel( -32768 ); //TODO why -32768 and not 0.0??? // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. boundsInitialized = this->CalculateClippedPlaneBounds( worldGeometry->GetReferenceGeometry(), planeGeometry, sliceBounds ); //TODO braucht man nicht immer } else { // Do we have an AbstractTransformGeometry? // This is the case for AbstractTransformGeometry's (e.g. a thin-plate-spline transform) const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(worldGeometry); if(abstractGeometry != NULL) { extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); mmPerPixel[0] = widthInMM / extent[0]; mmPerPixel[1] = heightInMM / extent[1]; origin = abstractGeometry->GetPlane()->GetOrigin(); right = abstractGeometry->GetPlane()->GetAxisVector(0); right.Normalize(); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); bottom.Normalize(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputGeometry->GetVtkTransform()->GetLinearInverse() ); composedResliceTransform->Concatenate( abstractGeometry->GetVtkAbstractTransform() ); localStorage->m_Reslicer->SetResliceTransform( composedResliceTransform ); composedResliceTransform->UnRegister( NULL ); // decrease RC // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see Geometry2DDataVtkMapper3D) localStorage->m_Reslicer->SetBackgroundLevel( -1023 ); } else { //no geometry => we can't reslice return; } } // Make sure that the image to display has a certain minimum size. if ( (extent[0] <= 2) && (extent[1] <= 2) ) { return; } // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ( (input->GetDimension() >= 3) && (input->GetDimension(2) > 1) ) { VtkResliceInterpolationProperty *resliceInterpolationProperty; this->GetDataNode()->GetProperty( resliceInterpolationProperty, "reslice interpolation" ); int interpolationMode = VTK_RESLICE_NEAREST; if ( resliceInterpolationProperty != NULL ) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch ( interpolationMode ) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationModeToNearestNeighbor(); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationModeToLinear(); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationModeToCubic(); break; } } else { localStorage->m_Reslicer->SetInterpolationModeToNearestNeighbor(); } //Begin Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if( inputData->GetNumberOfScalarComponents() == 1 ) // for now only single component are allowed { DataNode *dn=renderer->GetCurrentWorldGeometry2DNode(); if(dn) { ResliceMethodProperty *resliceMethodEnumProperty=0; if( dn->GetProperty( resliceMethodEnumProperty, "reslice.thickslices" ) && resliceMethodEnumProperty ) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty=0; if( dn->GetProperty( intProperty, "reslice.thickslices.num" ) && intProperty ) { thickSlicesNum = intProperty->GetValue(); if(thickSlicesNum < 1) thickSlicesNum=1; if(thickSlicesNum > 10) thickSlicesNum=10; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } localStorage->m_UnitSpacingImageFilter->SetInput( inputData ); localStorage->m_Reslicer->SetInput( localStorage->m_UnitSpacingImageFilter->GetOutput() ); //number of pixels per mm in x- and y-direction of the resampled Vector2D pixelsPerMM; pixelsPerMM[0] = 1.0 / mmPerPixel[0]; pixelsPerMM[1] = 1.0 / mmPerPixel[1]; //calulate the originArray and the orientations for the reslice-filter double originArray[3]; itk2vtk( origin, originArray ); localStorage->m_Reslicer->SetResliceAxesOrigin( originArray ); double cosines[9]; // direction of the X-axis of the sampled result vnl2vtk( right.Get_vnl_vector(), cosines ); // direction of the Y-axis of the sampled result vnl2vtk( bottom.Get_vnl_vector(), cosines + 3 );//fill next 3 elements // normal of the plane vnl2vtk( normal.Get_vnl_vector(), cosines + 6 );//fill the last 3 elements localStorage->m_Reslicer->SetResliceAxesDirectionCosines( cosines ); int xMin, xMax, yMin, yMax; if ( boundsInitialized ) { // Calculate output extent (integer values) xMin = static_cast< int >( sliceBounds[0] / mmPerPixel[0] + 0.5 ); xMax = static_cast< int >( sliceBounds[1] / mmPerPixel[0] + 0.5 ); yMin = static_cast< int >( sliceBounds[2] / mmPerPixel[1] + 0.5 ); yMax = static_cast< int >( sliceBounds[3] / mmPerPixel[1] + 0.5 ); } else { // If no reference geometry is available, we also don't know about the // maximum plane size; xMin = yMin = 0; xMax = static_cast< int >( extent[0] - pixelsPerMM[0] + 0.5); yMax = static_cast< int >( extent[1] - pixelsPerMM[1] + 0.5); } // Disallow huge dimensions if ( (xMax-xMin) * (yMax-yMin) > 4096*4096 ) { return; } // Calculate dataset spacing in plane z direction (NOT spacing of current // world geometry) double dataZSpacing = 1.0; Vector3D normInIndex; inputGeometry->WorldToIndex( origin, normal, normInIndex ); if(thickSlicesMode > 0) { dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality( 3 ); localStorage->m_Reslicer->SetOutputExtent( xMin, xMax-1, yMin, yMax-1, -thickSlicesNum, 0+thickSlicesNum ); } else { localStorage->m_Reslicer->SetOutputDimensionality( 2 ); localStorage->m_Reslicer->SetOutputExtent( xMin, xMax-1, yMin, yMax-1, 0, 0 ); } localStorage->m_Reslicer->SetOutputOrigin( 0.0, 0.0, 0.0 ); localStorage->m_Reslicer->SetOutputSpacing( mmPerPixel[0], mmPerPixel[1], dataZSpacing ); // xMax and yMax are meant exclusive until now, whereas // SetOutputExtent wants an inclusive bound. Thus, we need // to subtract 1. // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. // The reslicing result is used both for 2D and for 3D mapping. // The reslicing result is stored also for the 3D mapping. // Check the result vtkImageData* reslicedImage = 0; if(thickSlicesMode>0) { localStorage->m_TSFilter->SetThickSliceMode( thickSlicesMode-1 ); localStorage->m_TSFilter->SetInput( localStorage->m_Reslicer->GetOutput() ); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); reslicedImage = localStorage->m_TSFilter->GetOutput(); } else { localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); reslicedImage = localStorage->m_Reslicer->GetOutput(); } if((reslicedImage == NULL) || (reslicedImage->GetDataDimension() < 1)) { MITK_WARN << "reslicer returned empty image"; return; } // Store the result in a VTK image if ( localStorage->m_ReslicedImage == NULL ) { localStorage->m_ReslicedImage = vtkImageData::New(); } //TODO image is stored 2x. Do we still need that? rendererInfo.m_Image->DeepCopy( reslicedImage ); // localStorage->m_ReslicedImage->Update(); //TODO how does the reslicer know for which render window it is reslicing for? //set the current slice for the localStorage localStorage->m_ReslicedImage = reslicedImage; //set the current slice as texture for the plane localStorage->m_Texture->SetInput(localStorage->m_ReslicedImage); //set the size textured plane this->GeneratePlane( renderer, sliceBounds ); //turn the light out in the scene in order to render correct grey values. TODO How to turn it on if you need it? renderer->GetVtkRenderer()->RemoveAllLights(); //remove the VTK interaction renderer->GetVtkRenderer()->GetRenderWindow()->SetInteractor(NULL); //get the transformation matrix of the reslicer in order to render the slice as transversal, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); //apply the properties after the slice was set this->ApplyProperties( renderer, trans, mmPerPixel ); + vtkCamera* cam = renderer->GetVtkRenderer()->GetActiveCamera(); //set up the camera to view the transformed plane + MITK_INFO << "######################### vor"; + cam->Print(std::cout); + this->AdjustCamera( renderer ); renderer->GetVtkRenderer()->SetBackground(1, 1, 1); //Transform the camera to the current position (transveral, coronal and saggital plane). //This is necessary, because the vtkTransformFilter does not manipulate the vtkCamera. //(Without not all three planes would be visible). - renderer->GetVtkRenderer()->GetActiveCamera()->ApplyTransform(trans); +// renderer->GetVtkRenderer()->GetActiveCamera()->ApplyTransform(trans); // We have been modified + MITK_INFO << "######################### nach"; + cam->Print(std::cout); + localStorage->m_LastUpdateTime.Modified(); } bool mitk::ImageVtkMapper2D::LineIntersectZero( vtkPoints *points, int p1, int p2, vtkFloatingPointType *bounds ) { vtkFloatingPointType point1[3]; vtkFloatingPointType point2[3]; points->GetPoint( p1, point1 ); points->GetPoint( p2, point2 ); if ( (point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2]) ) { double x, y; x = ( point1[0] * point2[2] - point1[2] * point2[0] ) / ( point2[2] - point1[2] ); y = ( point1[1] * point2[2] - point1[2] * point2[1] ) / ( point2[2] - point1[2] ); if ( x < bounds[0] ) { bounds[0] = x; } if ( x > bounds[1] ) { bounds[1] = x; } if ( y < bounds[2] ) { bounds[2] = y; } if ( y > bounds[3] ) { bounds[3] = y; } bounds[4] = bounds[5] = 0.0; return true; } return false; } bool mitk::ImageVtkMapper2D::CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ) { // Clip the plane with the bounding geometry. To do so, the corner points // of the bounding box are transformed by the inverse transformation // matrix, and the transformed bounding box edges derived therefrom are // clipped with the plane z=0. The resulting min/max values are taken as // bounds for the image reslicer. const mitk::BoundingBox *boundingBox = boundingGeometry->GetBoundingBox(); mitk::BoundingBox::PointType bbMin = boundingBox->GetMinimum(); mitk::BoundingBox::PointType bbMax = boundingBox->GetMaximum(); vtkSmartPointer points = vtkSmartPointer::New(); if(boundingGeometry->GetImageGeometry()) { points->InsertPoint( 0, bbMin[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 1, bbMin[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 2, bbMin[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 3, bbMin[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 4, bbMax[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 5, bbMax[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 6, bbMax[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 7, bbMax[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); } else { points->InsertPoint( 0, bbMin[0], bbMin[1], bbMin[2] ); points->InsertPoint( 1, bbMin[0], bbMin[1], bbMax[2] ); points->InsertPoint( 2, bbMin[0], bbMax[1], bbMax[2] ); points->InsertPoint( 3, bbMin[0], bbMax[1], bbMin[2] ); points->InsertPoint( 4, bbMax[0], bbMin[1], bbMin[2] ); points->InsertPoint( 5, bbMax[0], bbMin[1], bbMax[2] ); points->InsertPoint( 6, bbMax[0], bbMax[1], bbMax[2] ); points->InsertPoint( 7, bbMax[0], bbMax[1], bbMin[2] ); } vtkSmartPointer newPoints = vtkSmartPointer::New(); vtkSmartPointer transform = vtkSmartPointer::New(); transform->Identity(); transform->Concatenate( planeGeometry->GetVtkTransform()->GetLinearInverse() ); transform->Concatenate( boundingGeometry->GetVtkTransform() ); transform->TransformPoints( points, newPoints ); bounds[0] = bounds[2] = 10000000.0; bounds[1] = bounds[3] = -10000000.0; bounds[4] = bounds[5] = 0.0; this->LineIntersectZero( newPoints, 0, 1, bounds ); this->LineIntersectZero( newPoints, 1, 2, bounds ); this->LineIntersectZero( newPoints, 2, 3, bounds ); this->LineIntersectZero( newPoints, 3, 0, bounds ); this->LineIntersectZero( newPoints, 0, 4, bounds ); this->LineIntersectZero( newPoints, 1, 5, bounds ); this->LineIntersectZero( newPoints, 2, 6, bounds ); this->LineIntersectZero( newPoints, 3, 7, bounds ); this->LineIntersectZero( newPoints, 4, 5, bounds ); this->LineIntersectZero( newPoints, 5, 6, bounds ); this->LineIntersectZero( newPoints, 6, 7, bounds ); this->LineIntersectZero( newPoints, 7, 4, bounds ); if ( (bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0) ) { return false; } else { // The resulting bounds must be adjusted by the plane spacing, since we // we have so far dealt with index coordinates const float *planeSpacing = planeGeometry->GetFloatSpacing(); bounds[0] *= planeSpacing[0]; bounds[1] *= planeSpacing[0]; bounds[2] *= planeSpacing[1]; bounds[3] *= planeSpacing[1]; bounds[4] *= planeSpacing[2]; bounds[5] *= planeSpacing[2]; return true; } } void mitk::ImageVtkMapper2D::GenerateAllData() { RendererInfoMap::iterator it, end = m_RendererInfo.end(); for ( it = m_RendererInfo.begin(); it != end; ++it) { this->Update( it->first ); } } void mitk::ImageVtkMapper2D::Clear() { RendererInfoMap::iterator it, end = m_RendererInfo.end(); for ( it = m_RendererInfo.begin(); it != end; ++it ) { it->second.RemoveObserver(); } m_RendererInfo.clear(); } void mitk::ImageVtkMapper2D::ApplyProperties(mitk::BaseRenderer* renderer, vtkSmartPointer transform, mitk::ScalarType mmPerPixel[2]) { //get the current localStorage for the corresponding renderer LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check for interpolation properties bool textureInterpolation = false; GetDataNode()->GetBoolProperty( "texture interpolation", textureInterpolation, renderer ); //set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); //do not repeat the texture (the image) localStorage->m_Texture->RepeatOff(); float rgb[3]= { 1.0f, 1.0f, 1.0f }; float opacity = 1.0f; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection //TODO do we need this? bool hover = false; bool selected = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); if(hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.hoveringcolor", renderer)); if(colorprop.IsNotNull()) memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); else GetColor( rgb, renderer ); } if(selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.selectedcolor", renderer)); if(colorprop.IsNotNull()) memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); else GetColor( rgb, renderer ); } if(!hover && !selected) { GetColor( rgb, renderer ); } //END TODO do we need this? // check for opacity prop and use it for rendering if it exists GetOpacity( opacity, renderer ); //set the opacity according to the properties localStorage->m_Actor->GetProperty()->SetOpacity(opacity); //get the binary property bool binary = false; this->GetDataNode()->GetBoolProperty( "binary", binary, renderer ); localStorage->m_Texture->SetMapColorScalarsThroughLookupTable(binary); //use color means that we want to use the color from the property list and not a lookuptable bool useColor = true; this->GetDataNode()->GetBoolProperty( "use color", useColor, renderer ); //the finalLookuptable will be used for the rendering and can either be a user-defined table or the default lut vtkSmartPointer finalLookuptable = vtkSmartPointer::New(); //BEGIN PROPERTY user-defined lut //currently we do not allow a lookuptable if it is a binary image bool useDefaultLut = true; if((!useColor) && (!binary)) { // If lookup table use is requested... mitk::LookupTableProperty::Pointer LookupTableProp; LookupTableProp = dynamic_cast (this->GetDataNode()->GetProperty("LookupTable")); //...check if there is a lookuptable provided by the user if ( LookupTableProp.IsNull() ) { MITK_WARN << "The use of a lookuptable is requested, but there is no lookuptable supplied by the user! The default lookuptable will be used instead."; } else { // If lookup table use is requested and supplied by the user: // only update the lut, when the properties have changed... if( LookupTableProp->GetLookupTable()->GetMTime() <= this->GetDataNode()->GetPropertyList()->GetMTime() ) { LookupTableProp->GetLookupTable()->ChangeOpacityForAll( opacity ); LookupTableProp->GetLookupTable()->ChangeOpacity(0, 0.0); } //we use the user-defined lookuptable finalLookuptable = LookupTableProp->GetLookupTable()->GetVtkLookupTable(); //we obtained a user-defined lut and dont have to use the default table useDefaultLut = false; } }//END PROPERTY user-defined lut //check if we need the default table if( useDefaultLut ) { finalLookuptable = localStorage->m_LookupTable; double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; //conversion to double for VTK localStorage->m_Actor->GetProperty()->SetColor(rgbConv); } else { //If the user defines a lut, we dont want to use the color and take white instead. localStorage->m_Actor->GetProperty()->SetColor(1.0, 1.0, 1.0); } bool binaryOutline = false; this->GetDataNode()->GetBoolProperty( "outline binary", binaryOutline, renderer ); localStorage->m_Mapper->ScalarVisibilityOn(); if ( binary ) { finalLookuptable->SetAlphaRange(0.0, 1.0); finalLookuptable->SetRange(0.0, 1.0); //0 is already mapped to transparent. //1 is now mapped to the current color and alpha if ( this->GetInput()->GetPixelType().GetBpe() <= 8 ) { if (binaryOutline) { //generate ontours/outlines TODO: not always necessary localStorage->m_OutlinePolyData = CreateOutlinePolyData(localStorage->m_ReslicedImage, mmPerPixel); float binaryOutlineWidth(1.0); if (this->GetDataNode()->GetFloatProperty( "outline width", binaryOutlineWidth, renderer )) { localStorage->m_Actor->GetProperty()->SetLineWidth(binaryOutlineWidth); } } } else { //TODO still true for MITK with VTK rendering? MITK_WARN << "Type of all binary images should be (un)signed char. Outline does not work on other pixel types!"; } } //END binary image handling else { LevelWindow levelWindow; this->GetLevelWindow( levelWindow, renderer ); //set up the lookuptable with the level window range finalLookuptable->SetRange( levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound() ); // obtain and apply opacity level window mitk::LevelWindow opacLevelWindow; if( this->GetLevelWindow( opacLevelWindow, renderer, "opaclevelwindow" ) ) { finalLookuptable->SetAlphaRange(opacLevelWindow.GetLowerWindowBound()/255.0, opacLevelWindow.GetLowerWindowBound()/255.0); } else { finalLookuptable->SetAlphaRange(0.0, 1.0); } } //use the finalLookuptable for mapping the colors localStorage->m_Texture->SetLookupTable( finalLookuptable ); //transform the plane to the corresponding view (transversal, coronal or saggital) localStorage->m_TransformFilter->SetTransform(transform); if(binaryOutline && binary) { localStorage->m_TransformFilter->SetInput(localStorage->m_OutlinePolyData); localStorage->m_Actor->SetTexture(NULL); } else { //transform the plane to the corresponding view (transversal, coronal or saggital) localStorage->m_TransformFilter->SetInputConnection(localStorage->m_Plane->GetOutputPort()); //set the texture for the actor localStorage->m_Actor->SetTexture(localStorage->m_Texture); } localStorage->m_TransformFilter->Update(); - localStorage->m_Mapper->SetInputConnection(localStorage->m_TransformFilter->GetOutputPort()); + localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); localStorage->m_Mapper->ScalarVisibilityOff(); } void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer* renderer) { if ( !this->IsVisible( renderer ) ) { return; } mitk::Image* data = const_cast( this->GetInput() ); if ( data == NULL ) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep( renderer ); // Check if time step is valid const TimeSlicedGeometry *dataTimeGeometry = data->GetTimeSlicedGeometry(); if ( ( dataTimeGeometry == NULL ) || ( dataTimeGeometry->GetTimeSteps() == 0 ) || ( !dataTimeGeometry->IsValidTime( this->GetTimestep() ) ) ) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //check if something important has changed and we need to rerender if ( (localStorage->m_LastUpdateTime < node->GetMTime()) //was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) //Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldGeometry2DUpdateTime()) //was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetDisplayGeometryUpdateTime()) // TODO this does not work || (localStorage->m_LastUpdateTime < renderer->GetDisplayGeometry()->GetMTime()) //was the display geometry modified? e.g. zooming, panning || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldGeometry2D()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ) { this->GenerateData( renderer ); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::DeleteRendererCallback( itk::Object *object, const itk::EventObject & ) { mitk::BaseRenderer *renderer = dynamic_cast< mitk::BaseRenderer* >( object ); if ( renderer ) { m_RendererInfo.erase( renderer ); } } mitk::ImageVtkMapper2D::RendererInfo::RendererInfo() : m_RendererID(-1), m_Renderer(NULL), m_Image(NULL), m_ObserverID( 0 ) { }; mitk::ImageVtkMapper2D::RendererInfo::~RendererInfo() { if ( m_Image != NULL ) { m_Image->Delete(); } } void mitk::ImageVtkMapper2D::RendererInfo::RemoveObserver() { if ( m_ObserverID != 0 ) { // m_ObserverID has to be decreased by one. Was incremented by one after creation to make the test m_ObserverID != 0 possible. m_Renderer->RemoveObserver( m_ObserverID-1 ); } } void mitk::ImageVtkMapper2D::RendererInfo::Initialize( int rendererID, mitk::BaseRenderer *renderer, unsigned long observerID ) { // increase ID by one to avoid 0 ID, has to be decreased before remove of the observer m_ObserverID = observerID+1; assert(rendererID>=0); assert(m_RendererID<0); m_Image = vtkImageData::New(); m_RendererID = rendererID; m_Renderer = renderer; } void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty( "use color", mitk::BoolProperty::New( true ), renderer, overwrite ); node->AddProperty( "outline binary", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "outline width", mitk::FloatProperty::New( 1.0 ), renderer, overwrite ); if(image->IsRotated()) node->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC) ); else node->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ); node->AddProperty( "texture interpolation", mitk::BoolProperty::New( mitk::DataNodeFactory::m_TextureInterpolationActive ) ); // set to user configurable default value (see global options) node->AddProperty( "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ); node->AddProperty( "bounding box", mitk::BoolProperty::New( false ) ); bool isBinaryImage(false); if ( ! node->GetBoolProperty("binary", isBinaryImage) ) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2)/2); sliceSelector->SetTimeNr(image->GetDimension(3)/2); sliceSelector->SetChannelNr(image->GetDimension(4)/2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); if ( centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized() ) { minValue = centralSliceImage->GetScalarValueMin(); maxValue = centralSliceImage->GetScalarValueMax(); min2ndValue = centralSliceImage->GetScalarValue2ndMin(); max2ndValue = centralSliceImage->GetScalarValue2ndMax(); } if ( minValue == maxValue ) { // centralSlice is strange, lets look at all data minValue = image->GetScalarValueMin(); maxValue = image->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetScalarValue2ndMaxNoRecompute(); } isBinaryImage = ( maxValue == min2ndValue && minValue == max2ndValue ); } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty( "opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite ); node->AddProperty( "color", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.selectedcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.selectedannotationcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.hoveringcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.hoveringannotationcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binary", mitk::BoolProperty::New( true ), renderer, overwrite ); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty( "opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite ); node->AddProperty( "color", ColorProperty::New(1.0,1.0,1.0), renderer, overwrite ); node->AddProperty( "binary", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); } if(image.IsNotNull() && image->IsInitialized()) { if((overwrite) || (node->GetProperty("levelwindow", renderer)==NULL)) { mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); mitk::LevelWindow levelwindow; levelwindow.SetAuto( image, true, true ); levWinProp->SetLevelWindow( levelwindow ); node->SetProperty( "levelwindow", levWinProp, renderer ); } if(((overwrite) || (node->GetProperty("opaclevelwindow", renderer)==NULL)) && (image->GetPixelType().GetItkTypeId() && *(image->GetPixelType().GetItkTypeId()) == typeid(itk::RGBAPixel))) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0,255); opaclevwin.SetWindowBounds(0,255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty( "opaclevelwindow", prop, renderer ); } if((overwrite) || (node->GetProperty("LookupTable", renderer)==NULL)) { // add a default rainbow lookup table for color mapping mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); vtkLookupTable* vtkLut = mitkLut->GetVtkLookupTable(); vtkLut->SetHueRange(0.6667, 0.0); vtkLut->SetTableRange(0.0, 20.0); vtkLut->Build(); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty( "LookupTable", mitkLutProp ); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } vtkSmartPointer mitk::ImageVtkMapper2D::CreateOutlinePolyData(vtkSmartPointer binarySlice, mitk::ScalarType mmPerPixel[2]){ int* dims = binarySlice->GetDimensions(); //dimensions of the image int line = dims[0]; //how many pixels per line? int x = 0; //pixel index x int y = 0; //pixel index y char* currentPixel; int nn = dims[0]*dims[1]; //max pixel(n,n) vtkSmartPointer points = vtkSmartPointer::New(); //the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); //the lines to connect the points for (int ii = 0; ii(binarySlice->GetScalarPointer(x, y, 0)); //if the current pixel value is set to something if (*currentPixel != 0) { //check in which direction a line is necessary if (ii >= line && *(currentPixel-line) == 0) { //x direction - bottom edge of the pixel //add the 2 points vtkIdType p1 = points->InsertNextPoint(x*mmPerPixel[0], y*mmPerPixel[1], 0); vtkIdType p2 = points->InsertNextPoint((x+1)*mmPerPixel[0], y*mmPerPixel[1], 0); //add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } if (ii <= nn-line && *(currentPixel+line) == 0) { //x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*mmPerPixel[0], (y+1)*mmPerPixel[1], 0); vtkIdType p2 = points->InsertNextPoint((x+1)*mmPerPixel[0], (y+1)*mmPerPixel[1], 0); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } if (ii > 1 && *(currentPixel-1) == 0) { //y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*mmPerPixel[0], y*mmPerPixel[1], 0); vtkIdType p2 = points->InsertNextPoint(x*mmPerPixel[0], (y+1)*mmPerPixel[1], 0); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } if (ii < nn-1 && *(currentPixel+1) == 0) { //y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x+1)*mmPerPixel[0], y*mmPerPixel[1], 0); vtkIdType p2 = points->InsertNextPoint((x+1)*mmPerPixel[0], (y+1)*mmPerPixel[1], 0); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } } //reached end of line x++; if (x >= line) { x = 0; y++; } } // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); return polyData; } mitk::ImageVtkMapper2D::LocalStorage::LocalStorage() { //TODO initialize everything with NULL in the list ??? m_ReslicedImage = vtkSmartPointer::New(); m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New(); m_LookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_TransformFilter = vtkSmartPointer::New(); m_Reslicer = vtkSmartPointer::New(); m_TSFilter = vtkSmartPointer::New(); m_UnitSpacingImageFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); + m_flag = true; //the following actions are always the same and thus can be performed //in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); m_Reslicer->ReleaseDataFlagOn(); m_UnitSpacingImageFilter->SetOutputSpacing( 1.0, 1.0, 1.0 ); //built a default lookuptable m_LookupTable->SetSaturationRange( 0.0, 0.0 ); m_LookupTable->SetHueRange( 0.0, 0.0 ); m_LookupTable->SetValueRange( 0.0, 1.0 ); m_LookupTable->Build(); //map all black values to transparent m_LookupTable->SetTableValue(0, 0.0, 0.0, 0.0, 0.0); //set the mapper for the actor m_Actor->SetMapper(m_Mapper); } diff --git a/Core/Code/Rendering/mitkImageVtkMapper2D.h b/Core/Code/Rendering/mitkImageVtkMapper2D.h index 6a96a01301..4581d2ce06 100644 --- a/Core/Code/Rendering/mitkImageVtkMapper2D.h +++ b/Core/Code/Rendering/mitkImageVtkMapper2D.h @@ -1,281 +1,283 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html 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 notices for more information. =========================================================================*/ #ifndef MITKIMAGEVTKMAPPER2D_H_HEADER_INCLUDED_C10E906E #define MITKIMAGEVTKMAPPER2D_H_HEADER_INCLUDED_C10E906E //MITK Rendering #include "mitkCommon.h" #include "mitkBaseRenderer.h" #include "mitkVtkMapper2D.h" //VTK #include class vtkActor; class vtkPolyDataMapper; class vtkPlaneSource; class vtkImageData; class vtkTransformPolyDataFilter; class vtkLookupTable; class vtkImageReslice; class vtkImageChangeInformation; class vtkPoints; class vtkMitkThickSlicesFilter; class vtkPolyData; namespace mitk { /** \brief Mapper to resample and display 2D slices of a 3D image. * * Currently implemented for mapping on PlaneGeometry and * AbstractTransformGeometry. The resulting 2D image (by reslicing the * underlying 3D input image appropriately) can either be directly rendered * in a 2D view or just be calculated to be used later on by another * rendering entity, e.g. in texture mapping in a 3D view. * * This results in a flipped version when used for texture mapping. Furthermore, * not the complete rectangular area described by the Geometry2D from the renderer * is resampled, @em if the Geometry2D is larger than the image dimension in the * requested direction. This results in a stretched version when used for texture * mapping. * * Properties that can be set for images and influence the imageMapper2D are: * * - \b "modality": (mitkModalityProperty) Modality of the image * - \b "opacity": (FloatProperty) Opacity of the image * - \b "color": (ColorProperty) Color of the image * - \b "use color": (BoolProperty) Use the color of the image or not * - \b "binary": (BoolProperty) is the image a binary image or not * - \b "outline binary": (BoolProperty) show outline of the image or not * - \b "texture interpolation": (BoolProperty) texture interpolation of the image * - \b "reslice interpolation": (VtkResliceInterpolationProperty) reslice interpolation of the image * - \b "in plane resample extent by geometry": (BoolProperty) Do it or not * - \b "bounding box": (BoolProperty) Is the Bounding Box of the image shown or not * - \b "layer": (IntProperty) Layer of the image * - \b "volume annotation color": (ColorProperty) color of the volume annotation * - \b "volume annotation unit": (StringProperty) annotation unit as string (does not implicit convert the unit!) unit is ml/cm3 * The default properties are: * - \b "opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite ) * - \b "color", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ) * - \b "use color", mitk::BoolProperty::New( true ), renderer, overwrite ) * - \b "binary", mitk::BoolProperty::New( true ), renderer, overwrite ) * - \b "outline binary", mitk::BoolProperty::New( false ), renderer, overwrite ) * - \b "texture interpolation", mitk::BoolProperty::New( mitk::DataNodeFactory::m_TextureInterpolationActive ) ) * - \b "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ) * - \b "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ) * - \b "bounding box", mitk::BoolProperty::New( false ) ) * - \b "layer", mitk::IntProperty::New(10), renderer, overwrite) * If the modality-property is set for an image, the mapper uses modality-specific default properties, * e.g. color maps, if they are defined. * \ingroup Mapper */ class MITK_CORE_EXPORT ImageVtkMapper2D : public VtkMapper2D { public: /** Standard class typedefs. */ mitkClassMacro( ImageVtkMapper2D,VtkMapper2D ); /** Method for creation through the object factory. */ itkNewMacro(Self); /** \brief Get the Image to map */ const mitk::Image *GetInput(void); /** \brief Calls Update() for all associated renderers. */ virtual void GenerateAllData(); /** \brief Checks whether this mapper needs to update itself and generate * data. */ virtual void Update(mitk::BaseRenderer * renderer); virtual void ApplyProperties(mitk::BaseRenderer* renderer, vtkSmartPointer transform, ScalarType mmPerPixel[2]); virtual vtkProp* GetVtkProp(mitk::BaseRenderer* renderer); virtual void MitkRenderOverlay(BaseRenderer* renderer); virtual void MitkRenderOpaqueGeometry(BaseRenderer* renderer); virtual void MitkRenderTranslucentGeometry(BaseRenderer* renderer); virtual void MitkRenderVolumetricGeometry(BaseRenderer* renderer); /** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */ class MITK_CORE_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage { public: /** \brief Actor of a 2D render window. */ vtkSmartPointer m_Actor; /** \brief Mapper of a 2D render window. */ vtkSmartPointer m_Mapper; /** \brief Current slice of a 2D render window. */ vtkSmartPointer m_ReslicedImage; /** \brief Plane on which the slice is rendered as texture. */ vtkSmartPointer m_Plane; /** \brief The texture which is used to render the current slice. */ vtkSmartPointer m_Texture; /** \brief The lookuptable for colors and level window */ vtkSmartPointer m_LookupTable; /** \brief transform the plane */ vtkSmartPointer m_TransformFilter; /** \brief The actual reslicer (one per renderer) */ vtkSmartPointer m_Reslicer; /** \brief Thickslices post filtering */ vtkSmartPointer m_TSFilter; /** \brief Using unit spacing for resampling makes life easier TODO improve docu ...*/ vtkSmartPointer m_UnitSpacingImageFilter; /** \brief PolyData object containg all lines/points needed for outlining the contour.*/ vtkSmartPointer m_OutlinePolyData; + bool m_flag; + /** \brief timestamp of last update of stored data */ itk::TimeStamp m_LastUpdateTime; /** \brief Constructor of the local storage. Do as much actions as possible in here to avoid double executions. */ LocalStorage(); ~LocalStorage() { } }; /** \brief This member holds all three LocalStorages for the three 2D render windows. */ mitk::Mapper::LocalStorageHandler m_LSH; /** \brief Internal storage class for data needed for rendering into a * renderer */ class MITK_CORE_EXPORT RendererInfo { /** \brief internal id of the renderer the data is stored for */ int m_RendererID; mitk::BaseRenderer* m_Renderer; public: /** \brief stores the id of the observer for delete event of renderer */ unsigned long m_ObserverID; vtkImageData* m_Image; RendererInfo(); ~RendererInfo(); inline bool IsInitialized() const { return m_RendererID >= 0; } void Initialize( int rendererID, mitk::BaseRenderer *renderer, unsigned long observerID ); inline int GetRendererID() const { return m_RendererID; } void RemoveObserver(); }; // RendererInfo /** \brief Get the RendererInfo for \a renderer */ const RendererInfo *GetRendererInfo( mitk::BaseRenderer *renderer ) { return &this->AccessRendererInfo(renderer); } /** \brief Release memory allocated for buffering */ virtual void Clear(); static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = NULL, bool overwrite = false); protected: //Generate a plane with size of the image in mm void GeneratePlane(mitk::BaseRenderer* renderer, vtkFloatingPointType planeBounds[6]); //set the camera to view the textured plane void AdjustCamera(mitk::BaseRenderer* renderer); vtkSmartPointer CreateOutlinePolyData(vtkSmartPointer binarySlice, ScalarType mmPerPixel[2]); ImageVtkMapper2D(); virtual ~ImageVtkMapper2D(); /** Does the actual resampling, without rendering the image yet. */ virtual void GenerateData(mitk::BaseRenderer *renderer); /** \brief Get the RendererInfo for @a renderer */ inline RendererInfo & AccessRendererInfo( mitk::BaseRenderer* renderer ) { RendererInfo& rendererInfo = m_RendererInfo[renderer]; if(rendererInfo.IsInitialized()==false) { // Add observer for renderer reset events (RendererInfo will // automatically be removed from list when a Renderer is deleted) // // Note: observer ID is passed to rendererInfo, which will take // responsiblity to remove the observer upon its destruction typedef itk::MemberCommand< ImageVtkMapper2D > MemberCommandType; MemberCommandType::Pointer deleteRendererCommand = MemberCommandType::New(); deleteRendererCommand->SetCallbackFunction( this, &ImageVtkMapper2D::DeleteRendererCallback ); unsigned long observerID = renderer->AddObserver( BaseRenderer::RendererResetEvent(), deleteRendererCommand ); // Initialize RendererInfo rendererInfo.Initialize( ImageVtkMapper2D::numRenderer++, renderer, observerID ); } return rendererInfo; } void DeleteRendererCallback( itk::Object *object, const itk::EventObject & ); bool LineIntersectZero( vtkPoints *points, int p1, int p2, vtkFloatingPointType *bounds ); bool CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ); /** \brief Number of renderers data is stored for * \todo General concept for keeping data for rendering required * \todo static? */ static int numRenderer; protected: typedef std::map RendererInfoMap; /** \brief Map of instances of RendererInfo * \sa RendererInfo */ RendererInfoMap m_RendererInfo; }; } // namespace mitk #endif /* MITKIMAGEVTKMAPPER2D_H_HEADER_INCLUDED_C10E906E */ diff --git a/Modules/IGT/IGTFilters/mitkCameraVisualization.cpp b/Modules/IGT/IGTFilters/mitkCameraVisualization.cpp index 85727a7a3b..d5d0828759 100644 --- a/Modules/IGT/IGTFilters/mitkCameraVisualization.cpp +++ b/Modules/IGT/IGTFilters/mitkCameraVisualization.cpp @@ -1,146 +1,147 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html 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 notices for more information. =========================================================================*/ #include "mitkCameraVisualization.h" #include "vtkCamera.h" #include "mitkPropertyList.h" #include "mitkProperties.h" mitk::CameraVisualization::CameraVisualization(): NavigationDataToNavigationDataFilter(), m_Renderer(NULL), m_FocalLength(10.0), m_ViewAngle(30.0) { // initialize members m_DirectionOfProjectionInToolCoordinates[0] = 0; m_DirectionOfProjectionInToolCoordinates[1] = 0; m_DirectionOfProjectionInToolCoordinates[2] = -1; m_ViewUpInToolCoordinates[0] = 1; m_ViewUpInToolCoordinates[1] = 0; m_ViewUpInToolCoordinates[2] = 0; } mitk::CameraVisualization::~CameraVisualization() { } void mitk::CameraVisualization::GenerateData() { // check if renderer was set if (m_Renderer.IsNull()) itkExceptionMacro(<< "Renderer was not properly set"); /* update outputs with tracking data from tools */ unsigned int numberOfInputs = this->GetNumberOfInputs(); for (unsigned int i = 0; i < numberOfInputs ; ++i) { mitk::NavigationData* output = this->GetOutput(i); assert(output); const mitk::NavigationData* input = this->GetInput(i); assert(input); if (input->IsDataValid() == false) { continue; } output->Graft(input); // First, copy all information from input to output } const NavigationData* navigationData = this->GetInput(); // get position from NavigationData to move the camera to this position Point3D cameraPosition = navigationData->GetPosition(); //calculate the transform from the quaternions static itk::QuaternionRigidTransform::Pointer quatTransform = itk::QuaternionRigidTransform::New(); mitk::NavigationData::OrientationType orientation = navigationData->GetOrientation(); // convert mitk::Scalartype quaternion to double quaternion because of itk bug vnl_quaternion doubleQuaternion(orientation.x(), orientation.y(), orientation.z(), orientation.r()); quatTransform->SetIdentity(); quatTransform->SetRotation(doubleQuaternion); quatTransform->Modified(); /* because of an itk bug, the transform can not be calculated with float datatype. To use it in the mitk geometry classes, it has to be transfered to mitk::ScalarType which is float */ static AffineTransform3D::MatrixType m; mitk::TransferMatrix(quatTransform->GetMatrix(), m); Vector3D directionOfProjection = m*m_DirectionOfProjectionInToolCoordinates; directionOfProjection.Normalize(); Point3D focalPoint = cameraPosition + m_FocalLength*directionOfProjection; // compute current view up vector Vector3D viewUp = m*m_ViewUpInToolCoordinates; m_Renderer->GetVtkRenderer()->GetActiveCamera()->SetPosition(cameraPosition[0],cameraPosition[1],cameraPosition[2]); m_Renderer->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(focalPoint[0],focalPoint[1],focalPoint[2]); m_Renderer->GetVtkRenderer()->GetActiveCamera()->SetViewUp(viewUp[0],viewUp[1],viewUp[2]); m_Renderer->GetVtkRenderer()->ResetCameraClippingRange(); m_Renderer->RequestUpdate(); + MITK_INFO << "mitkCameraVisualization.cpp"; } void mitk::CameraVisualization::SetRenderer(mitk::BaseRenderer* renderer) { m_Renderer = renderer; // if (m_Renderer) // { // m_Renderer->GetVtkRenderer()->GetActiveCamera()->Zoom(0.4); // } } const mitk::BaseRenderer* mitk::CameraVisualization::GetRenderer() { return m_Renderer; } void mitk::CameraVisualization::SetParameters( const mitk::PropertyList* p ) { if (p == NULL) return; mitk::Vector3D doP; if (p->GetPropertyValue("CameraVisualization_DirectionOfProjectionInToolCoordinates", doP) == true) // search for DirectionOfProjectionInToolCoordinates parameter this->SetDirectionOfProjectionInToolCoordinates(doP); // apply if found; mitk::Vector3D vUp; if (p->GetPropertyValue("CameraVisualization_ViewUpInToolCoordinates", vUp) == true) // search for ViewUpInToolCoordinates parameter this->SetViewUpInToolCoordinates(vUp); // apply if found; float fL; if (p->GetPropertyValue("CameraVisualization_FocalLength", fL) == true) // search for FocalLength parameter this->SetFocalLength(fL); // apply if found; float vA; if (p->GetPropertyValue("CameraVisualization_ViewAngle", vA) == true) // search for ViewAngle parameter this->SetFocalLength(vA); // apply if found; } mitk::PropertyList::ConstPointer mitk::CameraVisualization::GetParameters() const { mitk::PropertyList::Pointer p = mitk::PropertyList::New(); p->SetProperty("CameraVisualization_DirectionOfProjectionInToolCoordinates", mitk::Vector3DProperty::New(this->GetDirectionOfProjectionInToolCoordinates())); // store DirectionOfProjectionInToolCoordinates parameter p->SetProperty("CameraVisualization_ViewUpInToolCoordinates", mitk::Vector3DProperty::New(this->GetViewUpInToolCoordinates())); // store ViewUpInToolCoordinates parameter p->SetProperty("CameraVisualization_FocalLength", mitk::Vector3DProperty::New(this->GetFocalLength())); // store FocalLength parameter p->SetProperty("CameraVisualization_ViewAngle", mitk::Vector3DProperty::New(this->GetViewAngle())); // store ViewAngle parameter return mitk::PropertyList::ConstPointer(p); } diff --git a/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.cpp b/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.cpp index 9faddb5017..216ee052c0 100644 --- a/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.cpp +++ b/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.cpp @@ -1,7342 +1,7343 @@ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkMitkOpenGLGPUVolumeRayCastMapper.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 "vtkMitkOpenGLGPUVolumeRayCastMapper.h" // Only with VTK 5.6 or above #if ((VTK_MAJOR_VERSION > 5) || ((VTK_MAJOR_VERSION==5) && (VTK_MINOR_VERSION>=6) )) #include "vtkObjectFactory.h" #include "vtkVolume.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkCamera.h" #include "vtkMatrix4x4.h" #include "vtkImageData.h" #include "vtkTimerLog.h" #include "vtkVolumeProperty.h" #include "vtkColorTransferFunction.h" #include "vtkPiecewiseFunction.h" #include "vtkOpenGLExtensionManager.h" #include "vtkgl.h" #ifndef VTK_IMPLEMENT_MESA_CXX # include "vtkOpenGL.h" #endif #include #include #include #include #include #include "vtkClipDataSet.h" #include "vtkCellArray.h" #include "vtkDoubleArray.h" #include "vtkFloatArray.h" #include "vtkGeometryFilter.h" #include "vtkMath.h" #include "vtkPlane.h" #include "vtkPlaneCollection.h" #include "vtkPlanes.h" #include "vtkPolyData.h" #include "vtkPointData.h" #include "vtkCellData.h" #include "vtkPoints.h" #include "vtkUnsignedCharArray.h" #include "vtkUnsignedShortArray.h" #include "vtkUnsignedIntArray.h" #include "vtkUnstructuredGrid.h" #include "vtkVoxel.h" #include "vtkClipConvexPolyData.h" #include "vtkClipPolyData.h" #include "vtkDensifyPolyData.h" #include "vtkImageResample.h" #include #include // qsort() #include "vtkDataSetTriangleFilter.h" #include "vtkAbstractArray.h" // required if compiled against VTK 5.0 #include "vtkTessellatedBoxSource.h" #include "vtkCleanPolyData.h" #include "vtkCommand.h" // for VolumeMapperRender{Start|End|Progress}Event #include "vtkPerlinNoise.h" #include #include "vtkStdString.h" //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- class vtkUnsupportedRequiredExtensionsStringStream { public: vtkstd::ostringstream Stream; vtkUnsupportedRequiredExtensionsStringStream() { } private: // undefined copy constructor. vtkUnsupportedRequiredExtensionsStringStream(const vtkUnsupportedRequiredExtensionsStringStream &other); // undefined assignment operator. vtkUnsupportedRequiredExtensionsStringStream &operator=(const vtkUnsupportedRequiredExtensionsStringStream &other); }; class vtkMapDataArrayTextureId { public: vtkstd::map Map; vtkMapDataArrayTextureId() { } private: // undefined copy constructor. vtkMapDataArrayTextureId(const vtkMapDataArrayTextureId &other); // undefined assignment operator. vtkMapDataArrayTextureId &operator=(const vtkMapDataArrayTextureId &other); }; class vtkMapMaskTextureId { public: vtkstd::map Map; vtkMapMaskTextureId() { } private: // undefined copy constructor. vtkMapMaskTextureId(const vtkMapMaskTextureId &other); // undefined assignment operator. vtkMapMaskTextureId &operator=(const vtkMapMaskTextureId &other); }; //----------------------------------------------------------------------------- extern const char *vtkMitkGPUVolumeRayCastMapper_CompositeFS; extern const char *vtkMitkGPUVolumeRayCastMapper_CompositeCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_CompositeNoCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_HeaderFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPFourDependentFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPFourDependentCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPFourDependentNoCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPNoCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_ParallelProjectionFS; extern const char *vtkMitkGPUVolumeRayCastMapper_PerspectiveProjectionFS; extern const char *vtkMitkGPUVolumeRayCastMapper_ScaleBiasFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPFourDependentFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPFourDependentCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPFourDependentNoCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPNoCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_CompositeMaskFS; extern const char *vtkMitkGPUVolumeRayCastMapper_NoShadeFS; extern const char *vtkMitkGPUVolumeRayCastMapper_ShadeFS; extern const char *vtkMitkGPUVolumeRayCastMapper_OneComponentFS; extern const char *vtkMitkGPUVolumeRayCastMapper_FourComponentsFS; enum { vtkMitkOpenGLGPUVolumeRayCastMapperProjectionNotInitialized=-1, // not init vtkMitkOpenGLGPUVolumeRayCastMapperProjectionPerspective=0, // false vtkMitkOpenGLGPUVolumeRayCastMapperProjectionParallel=1 // true }; enum { vtkMitkOpenGLGPUVolumeRayCastMapperMethodNotInitialized, vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP, vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIPFourDependent, vtkMitkOpenGLGPUVolumeRayCastMapperMethodComposite, vtkMitkOpenGLGPUVolumeRayCastMapperMethodMinIP, vtkMitkOpenGLGPUVolumeRayCastMapperMethodMinIPFourDependent, vtkMitkOpenGLGPUVolumeRayCastMapperMethodCompositeMask }; // component implementation enum { vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotInitialized=-1, // not init vtkMitkOpenGLGPUVolumeRayCastMapperComponentOne=0, // false vtkMitkOpenGLGPUVolumeRayCastMapperComponentFour=1, // true vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotUsed=2 // when not composite }; // Shade implementation enum { vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotInitialized=-1, // not init vtkMitkOpenGLGPUVolumeRayCastMapperShadeNo=0, // false vtkMitkOpenGLGPUVolumeRayCastMapperShadeYes=1, // true vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotUsed=2 // when not composite }; // Cropping implementation enum { vtkMitkOpenGLGPUVolumeRayCastMapperCroppingNotInitialized, vtkMitkOpenGLGPUVolumeRayCastMapperCompositeCropping, vtkMitkOpenGLGPUVolumeRayCastMapperCompositeNoCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMIPCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMIPNoCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMIPFourDependentCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMIPFourDependentNoCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMinIPCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMinIPNoCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMinIPFourDependentCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMinIPFourDependentNoCropping }; enum { vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectDepthMap=0, // 2d texture vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront // 2d texture }; const int vtkMitkOpenGLGPUVolumeRayCastMapperNumberOfTextureObjects=vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+2; const int vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize=1024; //power of two #ifndef VTK_IMPLEMENT_MESA_CXX vtkCxxRevisionMacro(vtkMitkOpenGLGPUVolumeRayCastMapper, "$Revision: 1.9 $"); vtkStandardNewMacro(vtkMitkOpenGLGPUVolumeRayCastMapper); #endif //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- class vtkOpacityTable { public: vtkOpacityTable() { this->TextureId=0; this->LastBlendMode=vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND; this->LastSampleDistance=1.0; this->Table=0; this->Loaded=false; this->LastLinearInterpolation=false; } ~vtkOpacityTable() { if(this->TextureId!=0) { glDeleteTextures(1,&this->TextureId); this->TextureId=0; } if(this->Table!=0) { delete[] this->Table; this->Table=0; } } bool IsLoaded() { return this->Loaded; } void Bind() { assert("pre: uptodate" && this->Loaded); glBindTexture(GL_TEXTURE_1D,this->TextureId); } // \pre the active texture is set to TEXTURE2 void Update(vtkPiecewiseFunction *scalarOpacity, int blendMode, double sampleDistance, double range[2], double unitDistance, bool linearInterpolation) { assert("pre: scalarOpacity_exists" && scalarOpacity!=0); bool needUpdate=false; if(this->TextureId==0) { glGenTextures(1,&this->TextureId); needUpdate=true; } glBindTexture(GL_TEXTURE_1D,this->TextureId); if(needUpdate) { glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); } if(scalarOpacity->GetMTime() > this->BuildTime || (this->LastBlendMode!=blendMode) || (blendMode==vtkVolumeMapper::COMPOSITE_BLEND && this->LastSampleDistance!=sampleDistance) || needUpdate || !this->Loaded) { this->Loaded=false; if(this->Table==0) { this->Table= new float[vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize]; } scalarOpacity->GetTable(range[0],range[1], vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize, this->Table); this->LastBlendMode=blendMode; // Correct the opacity array for the spacing between the planes if we // are using a composite blending operation if(blendMode==vtkVolumeMapper::COMPOSITE_BLEND) { float *ptr=this->Table; double factor=sampleDistance/unitDistance; int i=0; while(i0.0001f) { *ptr=static_cast(1.0-pow(1.0-static_cast(*ptr), factor)); } ++ptr; ++i; } this->LastSampleDistance=sampleDistance; } glTexImage1D(GL_TEXTURE_1D,0,GL_ALPHA16, vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize,0, GL_ALPHA,GL_FLOAT,this->Table); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("1d opacity texture is too large"); this->Loaded=true; this->BuildTime.Modified(); } needUpdate=needUpdate || this->LastLinearInterpolation!=linearInterpolation; if(needUpdate) { this->LastLinearInterpolation=linearInterpolation; GLint value; if(linearInterpolation) { value=GL_LINEAR; } else { value=GL_NEAREST; } glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,value); glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,value); } } protected: GLuint TextureId; int LastBlendMode; double LastSampleDistance; vtkTimeStamp BuildTime; float *Table; bool Loaded; bool LastLinearInterpolation; }; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- class vtkOpacityTables { public: vtkstd::vector Vector; vtkOpacityTables(size_t numberOfLevels) : Vector(numberOfLevels) { } private: // undefined copy constructor. vtkOpacityTables(const vtkOpacityTables &other); // undefined assignment operator. vtkOpacityTables &operator=(const vtkOpacityTables &other); }; //----------------------------------------------------------------------------- class vtkRGBTable { public: vtkRGBTable() { this->TextureId=0; this->Table=0; this->Loaded=false; this->LastLinearInterpolation=false; } ~vtkRGBTable() { if(this->TextureId!=0) { glDeleteTextures(1,&this->TextureId); this->TextureId=0; } if(this->Table!=0) { delete[] this->Table; this->Table=0; } } bool IsLoaded() { return this->Loaded; } void Bind() { assert("pre: uptodate" && this->Loaded); glBindTexture(GL_TEXTURE_1D,this->TextureId); } // \pre the active texture is set properly. (default color, // mask1, mask2,..) void Update(vtkColorTransferFunction *scalarRGB, double range[2], bool linearInterpolation) { assert("pre: scalarRGB_exists" && scalarRGB!=0); bool needUpdate=false; if(this->TextureId==0) { glGenTextures(1,&this->TextureId); needUpdate=true; } glBindTexture(GL_TEXTURE_1D,this->TextureId); if(needUpdate) { glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); } if(scalarRGB->GetMTime() > this->BuildTime || needUpdate || !this->Loaded) { this->Loaded=false; if(this->Table==0) { this->Table= new float[vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize*3]; } scalarRGB->GetTable(range[0],range[1], vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize, this->Table); glTexImage1D(GL_TEXTURE_1D,0,GL_RGB16, vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize,0, GL_RGB,GL_FLOAT,this->Table); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("1d RGB texture is too large"); this->Loaded=true; this->BuildTime.Modified(); } needUpdate=needUpdate || this->LastLinearInterpolation!=linearInterpolation; if(needUpdate) { this->LastLinearInterpolation=linearInterpolation; GLint value; if(linearInterpolation) { value=GL_LINEAR; } else { value=GL_NEAREST; } glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,value); glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,value); } } protected: GLuint TextureId; vtkTimeStamp BuildTime; float *Table; bool Loaded; bool LastLinearInterpolation; }; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- class vtkKWScalarField { public: vtkKWScalarField() { this->TextureId=0; this->Loaded=false; this->Supports_GL_ARB_texture_float=false; this->LoadedTableRange[0]=0.0; this->LoadedTableRange[1]=1.0; this->LoadedExtent[0]=VTK_INT_MAX; this->LoadedExtent[1]=VTK_INT_MIN; this->LoadedExtent[2]=VTK_INT_MAX; this->LoadedExtent[3]=VTK_INT_MIN; this->LoadedExtent[4]=VTK_INT_MAX; this->LoadedExtent[5]=VTK_INT_MIN; } ~vtkKWScalarField() { if(this->TextureId!=0) { glDeleteTextures(1,&this->TextureId); this->TextureId=0; } } vtkTimeStamp GetBuildTime() { return this->BuildTime; } void Bind() { assert("pre: uptodate" && this->Loaded); glBindTexture(vtkgl::TEXTURE_3D,this->TextureId); } void Update(vtkImageData *input, int cellFlag, int textureExtent[6], int scalarMode, int arrayAccessMode, int arrayId, const char *arrayName, bool linearInterpolation, double tableRange[2], int maxMemoryInBytes) { bool needUpdate=false; bool modified=false; if(this->TextureId==0) { glGenTextures(1,&this->TextureId); needUpdate=true; } glBindTexture(vtkgl::TEXTURE_3D,this->TextureId); int obsolete=needUpdate || !this->Loaded || input->GetMTime()>this->BuildTime; if(!obsolete) { obsolete=cellFlag!=this->LoadedCellFlag; int i=0; while(!obsolete && i<6) { obsolete=obsolete || this->LoadedExtent[i]>textureExtent[i]; ++i; obsolete=obsolete || this->LoadedExtent[i]LoadedTableRange[0]!=tableRange[0] || this->LoadedTableRange[1]!=tableRange[1]; } if(obsolete) { this->Loaded=false; int dim[3]; input->GetDimensions(dim); GLint internalFormat=0; GLenum format=0; GLenum type=0; // shift then scale: y:=(x+shift)*scale double shift=0.0; double scale=1.0; int needTypeConversion=0; vtkDataArray *sliceArray=0; vtkDataArray *scalars= vtkAbstractMapper::GetScalars(input,scalarMode,arrayAccessMode, arrayId,arrayName, this->LoadedCellFlag); // DONT USE GetScalarType() or GetNumberOfScalarComponents() on // ImageData as it deals only with point data... int scalarType=scalars->GetDataType(); if(scalars->GetNumberOfComponents()==4) { // this is RGBA, unsigned char only internalFormat=GL_RGBA16; format=GL_RGBA; type=GL_UNSIGNED_BYTE; } else { // input->GetNumberOfScalarComponents()==1 switch(scalarType) { case VTK_FLOAT: if(this->Supports_GL_ARB_texture_float) { internalFormat=vtkgl::INTENSITY16F_ARB; } else { internalFormat=GL_INTENSITY16; } format=GL_RED; type=GL_FLOAT; shift=-tableRange[0]; scale=1/(tableRange[1]-tableRange[0]); break; case VTK_UNSIGNED_CHAR: internalFormat=GL_INTENSITY8; format=GL_RED; type=GL_UNSIGNED_BYTE; shift=-tableRange[0]/VTK_UNSIGNED_CHAR_MAX; scale= VTK_UNSIGNED_CHAR_MAX/(tableRange[1]-tableRange[0]); break; case VTK_SIGNED_CHAR: internalFormat=GL_INTENSITY8; format=GL_RED; type=GL_BYTE; shift=-(2*tableRange[0]+1)/VTK_UNSIGNED_CHAR_MAX; scale=VTK_SIGNED_CHAR_MAX/(tableRange[1]-tableRange[0]); break; case VTK_CHAR: // not supported assert("check: impossible case" && 0); break; case VTK_BIT: // not supported assert("check: impossible case" && 0); break; case VTK_ID_TYPE: // not supported assert("check: impossible case" && 0); break; case VTK_INT: internalFormat=GL_INTENSITY16; format=GL_RED; type=GL_INT; shift=-(2*tableRange[0]+1)/VTK_UNSIGNED_INT_MAX; scale=VTK_INT_MAX/(tableRange[1]-tableRange[0]); break; case VTK_DOUBLE: case VTK___INT64: case VTK_LONG: case VTK_LONG_LONG: case VTK_UNSIGNED___INT64: case VTK_UNSIGNED_LONG: case VTK_UNSIGNED_LONG_LONG: needTypeConversion=1; // to float if(this->Supports_GL_ARB_texture_float) { internalFormat=vtkgl::INTENSITY16F_ARB; } else { internalFormat=GL_INTENSITY16; } format=GL_RED; type=GL_FLOAT; shift=-tableRange[0]; scale=1/(tableRange[1]-tableRange[0]); sliceArray=vtkFloatArray::New(); break; case VTK_SHORT: internalFormat=GL_INTENSITY16; format=GL_RED; type=GL_SHORT; shift=-(2*tableRange[0]+1)/VTK_UNSIGNED_SHORT_MAX; scale=VTK_SHORT_MAX/(tableRange[1]-tableRange[0]); break; case VTK_STRING: // not supported assert("check: impossible case" && 0); break; case VTK_UNSIGNED_SHORT: internalFormat=GL_INTENSITY16; format=GL_RED; type=GL_UNSIGNED_SHORT; shift=-tableRange[0]/VTK_UNSIGNED_SHORT_MAX; scale= VTK_UNSIGNED_SHORT_MAX/(tableRange[1]-tableRange[0]); break; case VTK_UNSIGNED_INT: internalFormat=GL_INTENSITY16; format=GL_RED; type=GL_UNSIGNED_INT; shift=-tableRange[0]/VTK_UNSIGNED_INT_MAX; scale=VTK_UNSIGNED_INT_MAX/(tableRange[1]-tableRange[0]); break; default: assert("check: impossible case" && 0); break; } } // Enough memory? int textureSize[3]; int i=0; while(i<3) { textureSize[i]=textureExtent[2*i+1]-textureExtent[2*i]+1; ++i; } GLint width; glGetIntegerv(vtkgl::MAX_3D_TEXTURE_SIZE,&width); this->Loaded=textureSize[0]<=width && textureSize[1]<=width && textureSize[2]<=width; if(this->Loaded) { // so far, so good. the texture size is theorically small enough // for OpenGL vtkgl::TexImage3D(vtkgl::PROXY_TEXTURE_3D,0,internalFormat, textureSize[0],textureSize[1],textureSize[2],0, format,type,0); glGetTexLevelParameteriv(vtkgl::PROXY_TEXTURE_3D,0,GL_TEXTURE_WIDTH, &width); this->Loaded=width!=0; if(this->Loaded) { // so far, so good but some cards always succeed with a proxy texture // let's try to actually allocate.. vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,internalFormat,textureSize[0], textureSize[1],textureSize[2],0,format,type,0); GLenum errorCode=glGetError(); this->Loaded=errorCode!=GL_OUT_OF_MEMORY; if(this->Loaded) { // so far, so good, actual allocation succeeded. if(errorCode!=GL_NO_ERROR) { cout<<"after try to load the texture"; cout<<" ERROR (x"<(errorCode)); cout<Loaded=textureSize[0]*textureSize[1]* textureSize[2]*vtkAbstractArray::GetDataTypeSize(scalarType)* scalars->GetNumberOfComponents()<=maxMemoryInBytes; if(this->Loaded) { // OK, we consider the allocation above succeeded... // If it actually didn't the only to fix it for the user // is to decrease the value of this->MaxMemoryInBytes. // enough memory! We can load the scalars! double bias=shift*scale; // we don't clamp to edge because for the computation of the // gradient on the border we need some external value. glTexParameterf(vtkgl::TEXTURE_3D,vtkgl::TEXTURE_WRAP_R,vtkgl::CLAMP_TO_EDGE); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_WRAP_S,vtkgl::CLAMP_TO_EDGE); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_WRAP_T,vtkgl::CLAMP_TO_EDGE); GLfloat borderColor[4]={0.0,0.0,0.0,0.0}; glTexParameterfv(vtkgl::TEXTURE_3D,GL_TEXTURE_BORDER_COLOR, borderColor); if(needTypeConversion) { // Convert and send to the GPU, z-slice by z-slice. // Allocate memory on the GPU (NULL data pointer with the right // dimensions) // Here we are assuming that GL_ARB_texture_non_power_of_two is // available glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); // memory allocation is already done. // Send the slices: // allocate CPU memory for a slice. sliceArray->SetNumberOfComponents(1); // FB TODO CHECK THAT sliceArray->SetNumberOfTuples(textureSize[0]*textureSize[1]); void *slicePtr=sliceArray->GetVoidPointer(0); int k=0; int kInc=(dim[0]-cellFlag)*(dim[1]-cellFlag); int kOffset=(textureExtent[4]*(dim[1]-cellFlag) +textureExtent[2])*(dim[0]-cellFlag) +textureExtent[0]; while(kSetTuple1(jDestOffset+i, (scalars->GetTuple1(kOffset+jOffset +i) +shift)*scale); ++i; } ++j; jOffset+=dim[0]-cellFlag; jDestOffset+=textureSize[0]; } // Here we are assuming that GL_ARB_texture_non_power_of_two is // available vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D, 0, 0,0,k, textureSize[0],textureSize[1], 1, // depth is 1, not 0! format,type, slicePtr); ++k; kOffset+=kInc; } sliceArray->Delete(); } else { // One chunk of data to the GPU. // It works for the whole volume or for a subvolume. // Here we are assuming that GL_ARB_texture_non_power_of_two is // available // make sure any previous OpenGL call is executed and will not // be disturbed by our PixelTransfer value glFinish(); glPixelTransferf(GL_RED_SCALE,static_cast(scale)); glPixelTransferf(GL_RED_BIAS,static_cast(bias)); glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); if(!(textureExtent[1]-textureExtent[0]+cellFlag==dim[0])) { glPixelStorei(GL_UNPACK_ROW_LENGTH,dim[0]-cellFlag); } if(!(textureExtent[3]-textureExtent[2]+cellFlag==dim[1])) { glPixelStorei(vtkgl::UNPACK_IMAGE_HEIGHT_EXT, dim[1]-cellFlag); } void *dataPtr=scalars->GetVoidPointer( ((textureExtent[4]*(dim[1]-cellFlag)+textureExtent[2]) *(dim[0]-cellFlag)+textureExtent[0]) *scalars->GetNumberOfComponents()); if(1) // !this->SupportsPixelBufferObjects) { vtkgl::TexImage3D(vtkgl::TEXTURE_3D, 0, internalFormat, textureSize[0],textureSize[1],textureSize[2], 0,format,type,dataPtr); } else { GLuint pbo=0; vtkgl::GenBuffers(1,&pbo); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("genbuffer"); vtkgl::BindBuffer(vtkgl::PIXEL_UNPACK_BUFFER,pbo); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("binbuffer"); vtkgl::GLsizeiptr texSize= textureSize[0]*textureSize[1]*textureSize[2]* vtkAbstractArray::GetDataTypeSize(scalarType)* scalars->GetNumberOfComponents(); vtkgl::BufferData(vtkgl::PIXEL_UNPACK_BUFFER,texSize,dataPtr, vtkgl::STREAM_DRAW); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("bufferdata"); vtkgl::TexImage3D(vtkgl::TEXTURE_3D, 0, internalFormat, textureSize[0],textureSize[1],textureSize[2], 0,format,type,0); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("teximage3d"); vtkgl::BindBuffer(vtkgl::PIXEL_UNPACK_BUFFER,0); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("bindbuffer to 0"); vtkgl::DeleteBuffers(1,&pbo); } vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("3d texture is too large2"); // make sure TexImage3D is executed with our PixelTransfer mode glFinish(); // Restore the default values. glPixelStorei(GL_UNPACK_ROW_LENGTH,0); glPixelStorei(vtkgl::UNPACK_IMAGE_HEIGHT_EXT,0); glPixelTransferf(GL_RED_SCALE,1.0); glPixelTransferf(GL_RED_BIAS,0.0); } this->LoadedCellFlag=cellFlag; i=0; while(i<6) { this->LoadedExtent[i]=textureExtent[i]; ++i; } double spacing[3]; double origin[3]; input->GetSpacing(spacing); input->GetOrigin(origin); int swapBounds[3]; swapBounds[0]=(spacing[0]<0); swapBounds[1]=(spacing[1]<0); swapBounds[2]=(spacing[2]<0); if(!this->LoadedCellFlag) // loaded extents represent points { // slabsPoints[i]=(slabsDataSet[i] - origin[i/2]) / spacing[i/2]; // in general, x=o+i*spacing. // if spacing is positive min extent match the min of the // bounding box // and the max extent match the max of the bounding box // if spacing is negative min extent match the max of the // bounding box // and the max extent match the min of the bounding box // if spacing is negative, we may have to rethink the equation // between real point and texture coordinate... this->LoadedBounds[0]=origin[0]+ static_cast(this->LoadedExtent[0+swapBounds[0]])*spacing[0]; this->LoadedBounds[2]=origin[1]+ static_cast(this->LoadedExtent[2+swapBounds[1]])*spacing[1]; this->LoadedBounds[4]=origin[2]+ static_cast(this->LoadedExtent[4+swapBounds[2]])*spacing[2]; this->LoadedBounds[1]=origin[0]+ static_cast(this->LoadedExtent[1-swapBounds[0]])*spacing[0]; this->LoadedBounds[3]=origin[1]+ static_cast(this->LoadedExtent[3-swapBounds[1]])*spacing[1]; this->LoadedBounds[5]=origin[2]+ static_cast(this->LoadedExtent[5-swapBounds[2]])*spacing[2]; } else // loaded extents represent cells { int wholeTextureExtent[6]; input->GetExtent(wholeTextureExtent); i=1; while(i<6) { wholeTextureExtent[i]--; i+=2; } i=0; while(i<3) { if(this->LoadedExtent[2*i]==wholeTextureExtent[2*i]) { this->LoadedBounds[2*i+swapBounds[i]]=origin[i]; } else { this->LoadedBounds[2*i+swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i])+0.5)*spacing[i]; } if(this->LoadedExtent[2*i+1]==wholeTextureExtent[2*i+1]) { this->LoadedBounds[2*i+1-swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i+1])+1.0)*spacing[i]; } else { this->LoadedBounds[2*i+1-swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i+1])+0.5)*spacing[i]; } ++i; } } this->LoadedTableRange[0]=tableRange[0]; this->LoadedTableRange[1]=tableRange[1]; modified=true; } // if enough memory else { } } //load fail with out of memory else { } } // proxy ok else { // proxy failed } } else { // out of therical limitationa } } // if obsolete if(this->Loaded && (needUpdate || modified || linearInterpolation!=this->LinearInterpolation)) { this->LinearInterpolation=linearInterpolation; if(this->LinearInterpolation) { glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MAG_FILTER, GL_NEAREST ); } modified=true; } if(modified) { this->BuildTime.Modified(); } } double *GetLoadedBounds() { assert("pre: loaded" && this->Loaded); return this->LoadedBounds; } vtkIdType *GetLoadedExtent() { assert("pre: loaded" && this->Loaded); return this->LoadedExtent; } int GetLoadedCellFlag() { assert("pre: loaded" && this->Loaded); return this->LoadedCellFlag; } bool IsLoaded() { return this->Loaded; } bool GetSupports_GL_ARB_texture_float() { return this->Supports_GL_ARB_texture_float; } void SetSupports_GL_ARB_texture_float(bool value) { this->Supports_GL_ARB_texture_float=value; } protected: GLuint TextureId; vtkTimeStamp BuildTime; double LoadedBounds[6]; vtkIdType LoadedExtent[6]; int LoadedCellFlag; bool Loaded; bool LinearInterpolation; bool Supports_GL_ARB_texture_float; double LoadedTableRange[2]; }; //----------------------------------------------------------------------------- class vtkKWMask { public: vtkKWMask() { this->TextureId=0; this->Loaded=false; this->LoadedExtent[0]=VTK_INT_MAX; this->LoadedExtent[1]=VTK_INT_MIN; this->LoadedExtent[2]=VTK_INT_MAX; this->LoadedExtent[3]=VTK_INT_MIN; this->LoadedExtent[4]=VTK_INT_MAX; this->LoadedExtent[5]=VTK_INT_MIN; } ~vtkKWMask() { if(this->TextureId!=0) { glDeleteTextures(1,&this->TextureId); this->TextureId=0; } } vtkTimeStamp GetBuildTime() { return this->BuildTime; } // \pre vtkgl::ActiveTexture(vtkgl::TEXTURE7) has to be called first. void Bind() { assert("pre: uptodate" && this->Loaded); glBindTexture(vtkgl::TEXTURE_3D,this->TextureId); } // \pre vtkgl::ActiveTexture(vtkgl::TEXTURE7) has to be called first. void Update(vtkImageData *input, int cellFlag, int textureExtent[6], int scalarMode, int arrayAccessMode, int arrayId, const char *arrayName, int maxMemoryInBytes) { bool needUpdate=false; bool modified=false; if(this->TextureId==0) { glGenTextures(1,&this->TextureId); needUpdate=true; } glBindTexture(vtkgl::TEXTURE_3D,this->TextureId); int obsolete=needUpdate || !this->Loaded || input->GetMTime()>this->BuildTime; if(!obsolete) { obsolete=cellFlag!=this->LoadedCellFlag; int i=0; while(!obsolete && i<6) { obsolete=obsolete || this->LoadedExtent[i]>textureExtent[i]; ++i; obsolete=obsolete || this->LoadedExtent[i]Loaded=false; int dim[3]; input->GetDimensions(dim); vtkDataArray *scalars= vtkAbstractMapper::GetScalars(input,scalarMode,arrayAccessMode, arrayId,arrayName, this->LoadedCellFlag); // DONT USE GetScalarType() or GetNumberOfScalarComponents() on // ImageData as it deals only with point data... int scalarType=scalars->GetDataType(); if(scalarType!=VTK_UNSIGNED_CHAR) { cout <<"mask should be VTK_UNSIGNED_CHAR." << endl; } if(scalars->GetNumberOfComponents()!=1) { cout <<"mask should be a one-component scalar field." << endl; } GLint internalFormat=GL_ALPHA8; GLenum format=GL_ALPHA; GLenum type=GL_UNSIGNED_BYTE; // Enough memory? int textureSize[3]; int i=0; while(i<3) { textureSize[i]=textureExtent[2*i+1]-textureExtent[2*i]+1; ++i; } GLint width; glGetIntegerv(vtkgl::MAX_3D_TEXTURE_SIZE,&width); this->Loaded=textureSize[0]<=width && textureSize[1]<=width && textureSize[2]<=width; if(this->Loaded) { // so far, so good. the texture size is theorically small enough // for OpenGL vtkgl::TexImage3D(vtkgl::PROXY_TEXTURE_3D,0,internalFormat, textureSize[0],textureSize[1],textureSize[2],0, format,type,0); glGetTexLevelParameteriv(vtkgl::PROXY_TEXTURE_3D,0,GL_TEXTURE_WIDTH, &width); this->Loaded=width!=0; if(this->Loaded) { // so far, so good but some cards always succeed with a proxy texture // let's try to actually allocate.. vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,internalFormat,textureSize[0], textureSize[1],textureSize[2],0,format,type,0); GLenum errorCode=glGetError(); this->Loaded=errorCode!=GL_OUT_OF_MEMORY; if(this->Loaded) { // so far, so good, actual allocation succeeded. if(errorCode!=GL_NO_ERROR) { cout<<"after try to load the texture"; cout<<" ERROR (x"<(errorCode)); cout<Loaded=textureSize[0]*textureSize[1]* textureSize[2]*vtkAbstractArray::GetDataTypeSize(scalarType)* scalars->GetNumberOfComponents()<=maxMemoryInBytes; if(this->Loaded) { // OK, we consider the allocation above succeeded... // If it actually didn't the only to fix it for the user // is to decrease the value of this->MaxMemoryInBytes. // enough memory! We can load the scalars! // we don't clamp to edge because for the computation of the // gradient on the border we need some external value. glTexParameterf(vtkgl::TEXTURE_3D,vtkgl::TEXTURE_WRAP_R,vtkgl::CLAMP_TO_EDGE); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_WRAP_S,vtkgl::CLAMP_TO_EDGE); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_WRAP_T,vtkgl::CLAMP_TO_EDGE); GLfloat borderColor[4]={0.0,0.0,0.0,0.0}; glTexParameterfv(vtkgl::TEXTURE_3D,GL_TEXTURE_BORDER_COLOR, borderColor); glPixelTransferf(GL_ALPHA_SCALE,1.0); glPixelTransferf(GL_ALPHA_BIAS,0.0); glPixelStorei(GL_UNPACK_ALIGNMENT,1); if(!(textureExtent[1]-textureExtent[0]+cellFlag==dim[0])) { glPixelStorei(GL_UNPACK_ROW_LENGTH,dim[0]-cellFlag); } if(!(textureExtent[3]-textureExtent[2]+cellFlag==dim[1])) { glPixelStorei(vtkgl::UNPACK_IMAGE_HEIGHT_EXT, dim[1]-cellFlag); } void *dataPtr=scalars->GetVoidPointer( ((textureExtent[4]*(dim[1]-cellFlag)+textureExtent[2]) *(dim[0]-cellFlag)+textureExtent[0]) *scalars->GetNumberOfComponents()); vtkgl::TexImage3D(vtkgl::TEXTURE_3D, 0, internalFormat, textureSize[0],textureSize[1],textureSize[2], 0,format,type,dataPtr); // Restore the default values. glPixelStorei(GL_UNPACK_ROW_LENGTH,0); glPixelStorei(vtkgl::UNPACK_IMAGE_HEIGHT_EXT,0); glPixelTransferf(GL_ALPHA_SCALE,1.0); glPixelTransferf(GL_ALPHA_BIAS,0.0); this->LoadedCellFlag=cellFlag; i=0; while(i<6) { this->LoadedExtent[i]=textureExtent[i]; ++i; } double spacing[3]; double origin[3]; input->GetSpacing(spacing); input->GetOrigin(origin); int swapBounds[3]; swapBounds[0]=(spacing[0]<0); swapBounds[1]=(spacing[1]<0); swapBounds[2]=(spacing[2]<0); if(!this->LoadedCellFlag) // loaded extents represent points { // slabsPoints[i]=(slabsDataSet[i] - origin[i/2]) / spacing[i/2]; // in general, x=o+i*spacing. // if spacing is positive min extent match the min of the // bounding box // and the max extent match the max of the bounding box // if spacing is negative min extent match the max of the // bounding box // and the max extent match the min of the bounding box // if spacing is negative, we may have to rethink the equation // between real point and texture coordinate... this->LoadedBounds[0]=origin[0]+ static_cast(this->LoadedExtent[0+swapBounds[0]])*spacing[0]; this->LoadedBounds[2]=origin[1]+ static_cast(this->LoadedExtent[2+swapBounds[1]])*spacing[1]; this->LoadedBounds[4]=origin[2]+ static_cast(this->LoadedExtent[4+swapBounds[2]])*spacing[2]; this->LoadedBounds[1]=origin[0]+ static_cast(this->LoadedExtent[1-swapBounds[0]])*spacing[0]; this->LoadedBounds[3]=origin[1]+ static_cast(this->LoadedExtent[3-swapBounds[1]])*spacing[1]; this->LoadedBounds[5]=origin[2]+ static_cast(this->LoadedExtent[5-swapBounds[2]])*spacing[2]; } else // loaded extents represent cells { int wholeTextureExtent[6]; input->GetExtent(wholeTextureExtent); i=1; while(i<6) { wholeTextureExtent[i]--; i+=2; } i=0; while(i<3) { if(this->LoadedExtent[2*i]==wholeTextureExtent[2*i]) { this->LoadedBounds[2*i+swapBounds[i]]=origin[i]; } else { this->LoadedBounds[2*i+swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i])+0.5)*spacing[i]; } if(this->LoadedExtent[2*i+1]==wholeTextureExtent[2*i+1]) { this->LoadedBounds[2*i+1-swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i+1])+1.0)*spacing[i]; } else { this->LoadedBounds[2*i+1-swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i+1])+0.5)*spacing[i]; } ++i; } } modified=true; } // if enough memory else { } } //load fail with out of memory else { } } // proxy ok else { // proxy failed } } else { // out of therical limitationa } } // if obsolete if(this->Loaded && (needUpdate || modified)) { glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MAG_FILTER, GL_NEAREST ); modified=true; } if(modified) { this->BuildTime.Modified(); } } double *GetLoadedBounds() { assert("pre: loaded" && this->Loaded); return this->LoadedBounds; } vtkIdType *GetLoadedExtent() { assert("pre: loaded" && this->Loaded); return this->LoadedExtent; } int GetLoadedCellFlag() { assert("pre: loaded" && this->Loaded); return this->LoadedCellFlag; } bool IsLoaded() { return this->Loaded; } protected: GLuint TextureId; vtkTimeStamp BuildTime; double LoadedBounds[6]; vtkIdType LoadedExtent[6]; int LoadedCellFlag; bool Loaded; }; //----------------------------------------------------------------------------- // Display the status of the current framebuffer on the standard output. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::CheckFrameBufferStatus() { GLenum status; status = vtkgl::CheckFramebufferStatusEXT(vtkgl::FRAMEBUFFER_EXT); switch(status) { case 0: cout << "call to vtkgl::CheckFramebufferStatusEXT generates an error." << endl; break; case vtkgl::FRAMEBUFFER_COMPLETE_EXT: break; case vtkgl::FRAMEBUFFER_UNSUPPORTED_EXT: cout << "framebuffer is unsupported" << endl; break; case vtkgl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: cout << "framebuffer has an attachment error"<DisplayFrameBufferAttachments(); // this->DisplayReadAndDrawBuffers(); } //----------------------------------------------------------------------------- vtkStdString vtkMitkOpenGLGPUVolumeRayCastMapper::BufferToString(int buffer) { vtkStdString result; vtksys_ios::ostringstream ost; GLint size; GLint b=static_cast(buffer); switch(b) { case GL_NONE: ost << "GL_NONE"; break; case GL_FRONT_LEFT: ost << "GL_FRONT_LEFT"; break; case GL_FRONT_RIGHT: ost << "GL_FRONT_RIGHT"; break; case GL_BACK_LEFT: ost << "GL_BACK_LEFT"; break; case GL_BACK_RIGHT: ost << "GL_BACK_RIGHT"; break; case GL_FRONT: ost << "GL_FRONT"; break; case GL_BACK: ost << "GL_BACK"; break; case GL_LEFT: ost << "GL_LEFT"; break; case GL_RIGHT: ost << "GL_RIGHT"; break; case GL_FRONT_AND_BACK: ost << "GL_FRONT_AND_BACK"; break; default: glGetIntegerv(GL_AUX_BUFFERS,&size); if(buffer>=GL_AUX0 && buffer<(GL_AUX0+size)) { ost << "GL_AUX" << (buffer-GL_AUX0); } else { glGetIntegerv(vtkgl::MAX_COLOR_ATTACHMENTS_EXT,&size); if(static_cast(buffer)>=vtkgl::COLOR_ATTACHMENT0_EXT && static_cast(buffer)< (vtkgl::COLOR_ATTACHMENT0_EXT+static_cast(size))) { ost << "GL_COLOR_ATTACHMENT" << (static_cast(buffer)-vtkgl::COLOR_ATTACHMENT0_EXT) << "_EXT"; } else { ost << "unknown color buffer type=0x"<(value); vtkStdString s; GLenum i=0; while(iBufferToString(static_cast(value)); cout << "draw buffer " << i << "=" << s << endl; ++i; } glGetIntegerv(GL_READ_BUFFER,&value); s=this->BufferToString(static_cast(value)); cout << "read buffer=" << s << endl; } // ---------------------------------------------------------------------------- // Description: // Display all the attachments of the current framebuffer object. // ---------------------------------------------------------------------------- // // ---------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::DisplayFrameBufferAttachments() { GLint framebufferBinding; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&framebufferBinding); this->PrintError("after getting FRAMEBUFFER_BINDING_EXT"); if(framebufferBinding==0) { cout<<"Current framebuffer is bind to the system one"<(value); this->PrintError("after getting MAX_COLOR_ATTACHMENTS_EXT"); GLenum i=0; while(iDisplayFrameBufferAttachment(vtkgl::COLOR_ATTACHMENT0_EXT+i); ++i; } cout<<"depth attachement :"<DisplayFrameBufferAttachment(vtkgl::DEPTH_ATTACHMENT_EXT); cout<<"stencil attachement :"<DisplayFrameBufferAttachment(vtkgl::STENCIL_ATTACHMENT_EXT); } } // ---------------------------------------------------------------------------- // Description: // Display a given attachment for the current framebuffer object. //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::DisplayFrameBufferAttachment( unsigned int uattachment) { GLenum attachment=static_cast(uattachment); GLint params; vtkgl::GetFramebufferAttachmentParameterivEXT( vtkgl::FRAMEBUFFER_EXT,attachment, vtkgl::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT,¶ms); this->PrintError("after getting FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT"); switch(params) { case GL_NONE: cout<<" this attachment is empty"<PrintError("after getting FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT"); cout<<" this attachment is a texture with name: "<PrintError( "after getting FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT"); cout<<" its mipmap level is: "<PrintError( "after getting FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT"); if(params==0) { cout<<" this is not a cube map texture."<PrintError( "after getting FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT"); if(params==0) { cout<<" this is not 3D texture."<PrintError("after getting FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT"); cout<<" this attachment is a renderbuffer with name: "<(params)); this->PrintError( "after getting binding the current RENDERBUFFER_EXT to params"); vtkgl::GetRenderbufferParameterivEXT(vtkgl::RENDERBUFFER_EXT, vtkgl::RENDERBUFFER_WIDTH_EXT, ¶ms); this->PrintError("after getting RENDERBUFFER_WIDTH_EXT"); cout<<" renderbuffer width="<PrintError("after getting RENDERBUFFER_HEIGHT_EXT"); cout<<" renderbuffer height="<PrintError("after getting RENDERBUFFER_INTERNAL_FORMAT_EXT"); cout<<" renderbuffer internal format=0x"<< hex<PrintError("after getting RENDERBUFFER_RED_SIZE_EXT"); cout<<" renderbuffer actual resolution for the red component="<PrintError("after getting RENDERBUFFER_GREEN_SIZE_EXT"); cout<<" renderbuffer actual resolution for the green component="<PrintError("after getting RENDERBUFFER_BLUE_SIZE_EXT"); cout<<" renderbuffer actual resolution for the blue component="<PrintError("after getting RENDERBUFFER_ALPHA_SIZE_EXT"); cout<<" renderbuffer actual resolution for the alpha component="<PrintError("after getting RENDERBUFFER_DEPTH_SIZE_EXT"); cout<<" renderbuffer actual resolution for the depth component="<PrintError("after getting RENDERBUFFER_STENCIL_SIZE_EXT"); cout<<" renderbuffer actual resolution for the stencil component=" <(errorCode)) { case GL_NO_ERROR: result="No error"; break; case GL_INVALID_ENUM: result="Invalid enum"; break; case GL_INVALID_VALUE: result="Invalid value"; break; case GL_INVALID_OPERATION: result="Invalid operation"; break; case GL_STACK_OVERFLOW: result="stack overflow"; break; case GL_STACK_UNDERFLOW: result="stack underflow"; break; case GL_OUT_OF_MEMORY: result="out of memory"; break; case vtkgl::TABLE_TOO_LARGE: // GL_ARB_imaging result="Table too large"; break; case vtkgl::INVALID_FRAMEBUFFER_OPERATION_EXT: // GL_EXT_framebuffer_object, 310 result="invalid framebuffer operation ext"; break; case vtkgl::TEXTURE_TOO_LARGE_EXT: // GL_EXT_texture result="Texture too large"; break; default: result="unknown error"; } assert("post: result_exists" && result!=0); return result; } //----------------------------------------------------------------------------- // Display headerMessage on the standard output and the last OpenGL error // message if any. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError(const char *headerMessage) { GLenum errorCode=glGetError(); if(errorCode!=GL_NO_ERROR) { if ( headerMessage ) { cout<(errorCode)); cout<UnsupportedRequiredExtensions=0; this->OpenGLObjectsCreated=0; this->LoadExtensionsSucceeded=0; this->NumberOfFrameBuffers=0; this->m_BindMax = false; // up to 2 frame buffer 2D textures (left/right) // 1 dataset 3D texture // 1 colormap 1D texture // 1 opacitymap 1d texture // 1 grabbed depth buffer 2d texture int i=0; while(iTextureObjects[i]=0; ++i; } this->DepthRenderBufferObject=0; this->FrameBufferObject=0; for ( int j = 0; j < 8; j++ ) { for (i = 0; i < 3; i++ ) { this->BoundingBox[j][i] = 0.0; } } this->LastSize[0]=0; this->LastSize[1]=0; this->ReductionFactor = 1.0; this->Supports_GL_ARB_texture_float=0; this->SupportsPixelBufferObjects=0; i=0; while(i<3) { this->TempMatrix[i]=vtkMatrix4x4::New(); ++i; } this->ErrorLine=0; this->ErrorColumn=0; this->ErrorString=0; this->LastParallelProjection= vtkMitkOpenGLGPUVolumeRayCastMapperProjectionNotInitialized; this->LastRayCastMethod= vtkMitkOpenGLGPUVolumeRayCastMapperMethodNotInitialized; this->LastCroppingMode= vtkMitkOpenGLGPUVolumeRayCastMapperCroppingNotInitialized; this->LastComponent= vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotInitialized; this->LastShade=vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotInitialized; this->ClippedBoundingBox = NULL; this->SmallInput = NULL; this->MaxValueFrameBuffer=0; this->MaxValueFrameBuffer2=0; this->ReducedSize[0]=0; this->ReducedSize[1]=0; this->NumberOfCroppingRegions=0; this->PolyDataBoundingBox=0; this->Planes=0; this->NearPlane=0; this->Clip=0; this->Densify=0; this->InvVolumeMatrix=vtkMatrix4x4::New(); this->ScaleBiasProgramShader=0; this->UFrameBufferTexture=-1; this->UScale=-1; this->UBias=-1; this->SavedFrameBuffer=0; this->BoxSource=0; this->NoiseTexture=0; this->NoiseTextureSize=0; this->NoiseTextureId=0; this->IgnoreSampleDistancePerPixel=true; this->ScalarsTextures=new vtkMapDataArrayTextureId; this->MaskTextures=new vtkMapMaskTextureId; this->RGBTable=0; this->Mask1RGBTable=0; this->Mask2RGBTable=0; this->OpacityTables=0; this->CurrentScalar=0; this->CurrentMask=0; this->ActualSampleDistance=1.0; this->LastProgressEventTime=0.0; // date in seconds this->PreserveOrientation=true; } //----------------------------------------------------------------------------- // Destruct a vtkMitkOpenGLGPUVolumeRayCastMapper - clean up any memory used //----------------------------------------------------------------------------- vtkMitkOpenGLGPUVolumeRayCastMapper::~vtkMitkOpenGLGPUVolumeRayCastMapper() { if(this->UnsupportedRequiredExtensions!=0) { delete this->UnsupportedRequiredExtensions; this->UnsupportedRequiredExtensions=0; } int i=0; while(i<3) { this->TempMatrix[i]->Delete(); this->TempMatrix[i]=0; ++i; } if(this->ErrorString!=0) { delete[] this->ErrorString; this->ErrorString=0; } if ( this->SmallInput ) { this->SmallInput->UnRegister(this); } if(this->PolyDataBoundingBox!=0) { this->PolyDataBoundingBox->UnRegister(this); this->PolyDataBoundingBox=0; } if(this->Planes!=0) { this->Planes->UnRegister(this); this->Planes=0; } if(this->NearPlane!=0) { this->NearPlane->UnRegister(this); this->NearPlane=0; } if(this->Clip!=0) { this->Clip->UnRegister(this); this->Clip=0; } if(this->Densify!=0) { this->Densify->UnRegister(this); this->Densify=0; } if(this->BoxSource!=0) { this->BoxSource->UnRegister(this); this->BoxSource=0; } this->InvVolumeMatrix->UnRegister(this); this->InvVolumeMatrix=0; if(this->NoiseTexture!=0) { delete[] this->NoiseTexture; this->NoiseTexture=0; this->NoiseTextureSize=0; } if(this->ScalarsTextures!=0) { delete this->ScalarsTextures; this->ScalarsTextures=0; } if(this->MaskTextures!=0) { delete this->MaskTextures; this->MaskTextures=0; } } //----------------------------------------------------------------------------- // Based on hardware and properties, we may or may not be able to // render using 3D texture mapping. This indicates if 3D texture // mapping is supported by the hardware, and if the other extensions // necessary to support the specific properties are available. // //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::IsRenderSupported( vtkRenderWindow *window, vtkVolumeProperty *vtkNotUsed(property)) { window->MakeCurrent(); if(!this->LoadExtensionsSucceeded) { this->LoadExtensions(window); } if(!this->LoadExtensionsSucceeded) { vtkDebugMacro( "The following OpenGL extensions are required but not supported: " << (this->UnsupportedRequiredExtensions->Stream.str()).c_str()); return 0; } return 1; } //----------------------------------------------------------------------------- // Return if the required OpenGL extension `extensionName' is supported. // If not, its name is added to the string of unsupported but required // extensions. // \pre extensions_exist: extensions!=0 // \pre extensionName_exists: extensionName!=0 //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::TestRequiredExtension( vtkOpenGLExtensionManager *extensions, const char *extensionName) { assert("pre: extensions_exist" && extensions!=0); assert("pre: extensionName_exists" && extensionName!=0); int result=extensions->ExtensionSupported(extensionName); if(!result) { if(this->LoadExtensionsSucceeded) { this->UnsupportedRequiredExtensions->Stream<LoadExtensionsSucceeded=0; } else { this->UnsupportedRequiredExtensions->Stream<<", "<LoadExtensionsSucceeded will be set to 0 or 1 // - this->UnsupportedRequiredExtensions will have a message indicating // any failure codes //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::LoadExtensions( vtkRenderWindow *window) { // We may already have a string stream for the unsupported extensions // from the last time this method was called. If so, delete it. if(this->UnsupportedRequiredExtensions!=0) { delete this->UnsupportedRequiredExtensions; } // Create a string stream to hold the unsupported extensions so we can // report something meaningful back this->UnsupportedRequiredExtensions = new vtkUnsupportedRequiredExtensionsStringStream; // It does not work on Apple OS X Snow Leopard with nVidia. // There is a bug in the OpenGL driver with an error in the // Cg compiler about an infinite loop. #ifdef __APPLE__ this->LoadExtensionsSucceeded=0; return; #endif // Assume success this->LoadExtensionsSucceeded=1; const char *gl_vendor=reinterpret_cast(glGetString(GL_VENDOR)); /* if(strstr(gl_vendor,"ATI")!=0) { this->LoadExtensionsSucceeded=0; return; }*/ const char *gl_version=reinterpret_cast(glGetString(GL_VERSION)); if(strstr(gl_version,"Mesa")!=0) { // - GL_VENDOR cannot be used because it can be "Brian Paul" or // "Mesa project" // - GL_RENDERER cannot be used because it can be "Software Rasterizer" or // "Mesa X11" // - GL_VERSION is more robust. It has things like "2.0 Mesa 7.0.4" or // "2.1 Mesa 7.2" or "2.1 Mesa 7.3-devel" // Mesa does not work with multiple draw buffers: // "framebuffer has bad draw buffer" // "render clipped 1 ERROR (x506) invalid framebuffer operation ext" this->LoadExtensionsSucceeded=0; return; } // Create an extension manager vtkOpenGLExtensionManager *extensions=vtkOpenGLExtensionManager::New(); extensions->SetRenderWindow(window); // GL_ARB_draw_buffers requires OpenGL 1.3, so we must have OpenGL 1.3 // We don't need to check for some extensions that become part of OpenGL // core after 1.3. Among them: // - texture_3d is in core OpenGL since 1.2 // - texture_edge_clamp is in core OpenGL since 1.2 // (GL_SGIS_texture_edge_clamp or GL_EXT_texture_edge_clamp (nVidia) ) // - multitexture is in core OpenGL since 1.3 int supports_GL_1_3=extensions->ExtensionSupported("GL_VERSION_1_3"); int supports_GL_2_0=0; // No 1.3 support - give up if(!supports_GL_1_3) { this->LoadExtensionsSucceeded=0; this->UnsupportedRequiredExtensions->Stream<< " OpenGL 1.3 is required but not supported"; extensions->Delete(); return; } // Check for 2.0 support supports_GL_2_0=extensions->ExtensionSupported("GL_VERSION_2_0"); // Some extensions that are supported in 2.0, but if we don't // have 2.0 we'll need to check further int supports_shading_language_100 = 1; int supports_shader_objects = 1; int supports_fragment_shader = 1; int supports_texture_non_power_of_two = 1; int supports_draw_buffers = 1; if(!supports_GL_2_0) { supports_shading_language_100= extensions->ExtensionSupported("GL_ARB_shading_language_100"); supports_shader_objects= extensions->ExtensionSupported("GL_ARB_shader_objects"); supports_fragment_shader= extensions->ExtensionSupported("GL_ARB_fragment_shader"); supports_texture_non_power_of_two= extensions->ExtensionSupported("GL_ARB_texture_non_power_of_two"); supports_draw_buffers= extensions->ExtensionSupported("GL_ARB_draw_buffers"); } // We have to check for framebuffer objects int supports_GL_EXT_framebuffer_object= extensions->ExtensionSupported("GL_EXT_framebuffer_object" ); // Find out if we have OpenGL 1.4 support int supports_GL_1_4=extensions->ExtensionSupported("GL_VERSION_1_4"); // Find out if we have the depth texture ARB extension int supports_GL_ARB_depth_texture= extensions->ExtensionSupported("GL_ARB_depth_texture"); // Depth textures are support if we either have OpenGL 1.4 // or if the depth texture ARB extension is supported int supports_depth_texture = supports_GL_1_4 || supports_GL_ARB_depth_texture; // Now start adding messages to the UnsupportedRequiredExtensions string // Log message if shading language 100 is not supported if(!supports_shading_language_100) { this->UnsupportedRequiredExtensions->Stream<< " shading_language_100 (or OpenGL 2.0) is required but not supported"; this->LoadExtensionsSucceeded=0; } else { // We can query the GLSL version, we need >=1.20 const char *glsl_version= reinterpret_cast(glGetString(vtkgl::SHADING_LANGUAGE_VERSION)); int glslMajor, glslMinor; vtksys_ios::istringstream ist(glsl_version); ist >> glslMajor; char c; ist.get(c); // '.' ist >> glslMinor; //sscanf(version, "%d.%d", &glslMajor, &glslMinor); if(glslMajor<1 || (glslMajor==1 && glslMinor<20)) { this->LoadExtensionsSucceeded=0; } } // Log message if shader objects are not supported if(!supports_shader_objects) { this->UnsupportedRequiredExtensions->Stream<< " shader_objects (or OpenGL 2.0) is required but not supported"; this->LoadExtensionsSucceeded=0; } // Log message if fragment shaders are not supported if(!supports_fragment_shader) { this->UnsupportedRequiredExtensions->Stream<< " fragment_shader (or OpenGL 2.0) is required but not supported"; this->LoadExtensionsSucceeded=0; } // Log message if non power of two textures are not supported if(!supports_texture_non_power_of_two) { this->UnsupportedRequiredExtensions->Stream<< " texture_non_power_of_two (or OpenGL 2.0) is required but not " << "supported"; this->LoadExtensionsSucceeded=0; } // Log message if draw buffers are not supported if(!supports_draw_buffers) { this->UnsupportedRequiredExtensions->Stream<< " draw_buffers (or OpenGL 2.0) is required but not supported"; this->LoadExtensionsSucceeded=0; } // Log message if depth textures are not supported if(!supports_depth_texture) { this->UnsupportedRequiredExtensions->Stream<< " depth_texture (or OpenGL 1.4) is required but not supported"; this->LoadExtensionsSucceeded=0; } // Log message if framebuffer objects are not supported if(!supports_GL_EXT_framebuffer_object) { this->UnsupportedRequiredExtensions->Stream<< " framebuffer_object is required but not supported"; this->LoadExtensionsSucceeded=0; } // Have we succeeded so far? If not, just return. if(!this->LoadExtensionsSucceeded) { extensions->Delete(); return; } // Now start loading the extensions // First load all 1.2 and 1.3 extensions (we know we // support at least up to 1.3) extensions->LoadExtension("GL_VERSION_1_2"); extensions->LoadExtension("GL_VERSION_1_3"); // Load the 2.0 extensions if supported if(supports_GL_2_0) { extensions->LoadExtension("GL_VERSION_2_0"); } // Otherwise, we'll need to specifically load the // shader objects, fragment shader, and draw buffers // extensions else { extensions->LoadCorePromotedExtension("GL_ARB_shader_objects"); extensions->LoadCorePromotedExtension("GL_ARB_fragment_shader"); extensions->LoadCorePromotedExtension("GL_ARB_draw_buffers"); } // Load the framebuffer object extension extensions->LoadExtension("GL_EXT_framebuffer_object"); // Optional extension (does not fail if not present) // Load it if supported which will allow us to store // textures as floats this->Supports_GL_ARB_texture_float= extensions->ExtensionSupported("GL_ARB_texture_float" ); if(this->Supports_GL_ARB_texture_float) { extensions->LoadExtension( "GL_ARB_texture_float" ); } // Optional extension (does not fail if not present) // Used to minimize memory footprint when loading large 3D textures // of scalars. // VBO or 1.5 is required by PBO or 2.1 int supports_GL_1_5=extensions->ExtensionSupported("GL_VERSION_1_5"); int supports_vertex_buffer_object=supports_GL_1_5 || extensions->ExtensionSupported("GL_ARB_vertex_buffer_object"); int supports_GL_2_1=extensions->ExtensionSupported("GL_VERSION_2_1"); this->SupportsPixelBufferObjects=supports_vertex_buffer_object && (supports_GL_2_1 || extensions->ExtensionSupported("GL_ARB_pixel_buffer_object")); if(this->SupportsPixelBufferObjects) { if(supports_GL_1_5) { extensions->LoadExtension("GL_VERSION_1_5"); } else { extensions->LoadCorePromotedExtension("GL_ARB_vertex_buffer_object"); } if(supports_GL_2_1) { extensions->LoadExtension("GL_VERSION_2_1"); } else { extensions->LoadCorePromotedExtension("GL_ARB_pixel_buffer_object"); } } // Ultimate test. Some old cards support OpenGL 2.0 but not while // statements in a fragment shader (example: nVidia GeForce FX 5200) // It does not fail when compiling each shader source but at linking // stage because the parser underneath only check for syntax during // compilation and the actual native code generation happens during // the linking stage. this->CreateGLSLObjects(); this->NumberOfCroppingRegions=1; this->BuildProgram(1,vtkMitkOpenGLGPUVolumeRayCastMapperMethodComposite, vtkMitkOpenGLGPUVolumeRayCastMapperShadeNo, vtkMitkOpenGLGPUVolumeRayCastMapperComponentOne); GLint params; vtkgl::GetProgramiv(static_cast(this->ProgramShader), vtkgl::LINK_STATUS,¶ms); if(params==GL_FALSE) { this->LoadExtensionsSucceeded=0; this->UnsupportedRequiredExtensions->Stream<< " this card does not support while statements in fragment shaders."; } // FB debug this->CheckLinkage(this->ProgramShader); // Release GLSL Objects. GLuint programShader=static_cast(this->ProgramShader); vtkgl::DeleteProgram(programShader); this->LastParallelProjection= vtkMitkOpenGLGPUVolumeRayCastMapperProjectionNotInitialized; this->LastRayCastMethod= vtkMitkOpenGLGPUVolumeRayCastMapperMethodNotInitialized; this->LastCroppingMode= vtkMitkOpenGLGPUVolumeRayCastMapperCroppingNotInitialized; this->LastComponent= vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotInitialized; this->LastShade=vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotInitialized; extensions->Delete(); } //----------------------------------------------------------------------------- // Create GLSL OpenGL objects such fragment program Ids. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::CreateGLSLObjects() { GLuint programShader; GLuint fragmentMainShader; programShader=vtkgl::CreateProgram(); fragmentMainShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::AttachShader(programShader,fragmentMainShader); vtkgl::DeleteShader(fragmentMainShader); // reference counting vtkgl::ShaderSource( fragmentMainShader,1, const_cast(&vtkMitkGPUVolumeRayCastMapper_HeaderFS),0); vtkgl::CompileShader(fragmentMainShader); this->CheckCompilation(static_cast(fragmentMainShader)); GLuint fragmentProjectionShader; GLuint fragmentTraceShader; GLuint fragmentCroppingShader; GLuint fragmentComponentShader; GLuint fragmentShadeShader; fragmentProjectionShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::AttachShader(programShader,fragmentProjectionShader); vtkgl::DeleteShader(fragmentProjectionShader); // reference counting fragmentTraceShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::AttachShader(programShader,fragmentTraceShader); vtkgl::DeleteShader(fragmentTraceShader); // reference counting fragmentCroppingShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::AttachShader(programShader,fragmentCroppingShader); vtkgl::DeleteShader(fragmentCroppingShader); // reference counting fragmentComponentShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); // don't delete it, it is optionally attached. fragmentShadeShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); // Save GL objects by static casting to standard C types. GL* types // are not allowed in VTK header files. this->ProgramShader=static_cast(programShader); this->FragmentMainShader=static_cast(fragmentMainShader); this->FragmentProjectionShader= static_cast(fragmentProjectionShader); this->FragmentTraceShader=static_cast(fragmentTraceShader); this->FragmentCroppingShader= static_cast(fragmentCroppingShader); this->FragmentComponentShader= static_cast(fragmentComponentShader); this->FragmentShadeShader= static_cast(fragmentShadeShader); } void vtkMitkOpenGLGPUVolumeRayCastMapper::BindFramebuffer() { vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT,GL_TEXTURE_2D, this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront], 0); GLenum err = glGetError(); if(m_BindMax) { vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D,this->MaxValueFrameBuffer,0); } } //----------------------------------------------------------------------------- // Create OpenGL objects such as textures, buffers and fragment program Ids. // It only registers Ids, there is no actual initialization of textures or // fragment program. // // Pre-conditions: // This method assumes that this->LoadedExtensionsSucceeded is 1. // // Post-conditions: // When this method completes successfully, this->OpenGLObjectsCreated // will be 1. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::CreateOpenGLObjects() { // Do nothing if the OpenGL objects have already been created if ( this->OpenGLObjectsCreated ) { return; } // We need only two color buffers (ping-pong) this->NumberOfFrameBuffers=2; // TODO: clean this up! // 2*Frame buffers(2d textures)+colorMap (1d texture) +dataset (3d texture) // + opacitymap (1d texture) + grabbed depthMap (2d texture) // Frame buffers(2d textures)+colorMap (1d texture) +dataset (3d texture) // + opacity (1d texture)+grabbed depth buffer (2d texture) GLuint frameBufferObject; GLuint depthRenderBufferObject; GLuint textureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperNumberOfTextureObjects]; // Create the various objects we will need - one frame buffer // which will contain a render buffer for depth and a texture // for color. vtkgl::GenFramebuffersEXT(1, &frameBufferObject); // color vtkgl::GenRenderbuffersEXT(1, &depthRenderBufferObject); // depth glGenTextures(vtkMitkOpenGLGPUVolumeRayCastMapperNumberOfTextureObjects,textureObjects); // Color buffers GLint value; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&value); GLuint savedFrameBuffer=static_cast(value); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT,frameBufferObject); // Depth buffer vtkgl::BindRenderbufferEXT(vtkgl::RENDERBUFFER_EXT, depthRenderBufferObject); vtkgl::FramebufferRenderbufferEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::DEPTH_ATTACHMENT_EXT, vtkgl::RENDERBUFFER_EXT, depthRenderBufferObject); // Restore default frame buffer. vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT,savedFrameBuffer); this->CreateGLSLObjects(); // Save GL objects by static casting to standard C types. GL* types // are not allowed in VTK header files. this->FrameBufferObject=static_cast(frameBufferObject); this->DepthRenderBufferObject=static_cast(depthRenderBufferObject); int i=0; while(iTextureObjects[i]=static_cast(textureObjects[i]); ++i; } this->OpenGLObjectsCreated=1; } //----------------------------------------------------------------------------- // Check the compilation status of some fragment shader source. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::CheckCompilation( unsigned int fragmentShader) { GLuint fs=static_cast(fragmentShader); GLint params; vtkgl::GetShaderiv(fs,vtkgl::COMPILE_STATUS,¶ms); if(params==GL_TRUE) { vtkDebugMacro(<<"shader source compiled successfully"); } else { vtkErrorMacro(<<"shader source compile error"); // include null terminator vtkgl::GetShaderiv(fs,vtkgl::INFO_LOG_LENGTH,¶ms); if(params>0) { char *buffer=new char[params]; vtkgl::GetShaderInfoLog(fs,params,0,buffer); vtkErrorMacro(<<"log: "<(programShader); // info about the list of active uniform variables vtkgl::GetProgramiv(prog,vtkgl::ACTIVE_UNIFORMS,¶ms); cout<<"There are "<(params); vtkgl::GetProgramiv(prog,vtkgl::OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB, ¶ms); GLint buffSize=params; char *name=new char[buffSize+1]; GLint size; GLenum type; while(i(programShader); vtkgl::GetProgramiv(prog,vtkgl::LINK_STATUS,¶ms); int status = 0; if(params==GL_TRUE) { status = 1; vtkDebugMacro(<<"program linked successfully"); } else { vtkErrorMacro(<<"program link error"); vtkgl::GetProgramiv(prog,vtkgl::INFO_LOG_LENGTH,¶ms); if(params>0) { char *buffer=new char[params]; vtkgl::GetProgramInfoLog(prog,params,0,buffer); vtkErrorMacro(<<"log: "<OpenGLObjectsCreated==0 //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::ReleaseGraphicsResources( vtkWindow *window) { if(this->OpenGLObjectsCreated) { window->MakeCurrent(); this->LastSize[0]=0; this->LastSize[1]=0; GLuint frameBufferObject=static_cast(this->FrameBufferObject); vtkgl::DeleteFramebuffersEXT(1,&frameBufferObject); GLuint depthRenderBufferObject= static_cast(this->DepthRenderBufferObject); vtkgl::DeleteRenderbuffersEXT(1,&depthRenderBufferObject); GLuint textureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperNumberOfTextureObjects]; int i=0; while(i<(vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+this->NumberOfFrameBuffers)) { textureObjects[i]=static_cast(this->TextureObjects[i]); ++i; } glDeleteTextures(vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+this->NumberOfFrameBuffers,textureObjects); if(this->MaxValueFrameBuffer!=0) { GLuint maxValueFrameBuffer= static_cast(this->MaxValueFrameBuffer); glDeleteTextures(1,&maxValueFrameBuffer); this->MaxValueFrameBuffer=0; } if(this->MaxValueFrameBuffer2!=0) { GLuint maxValueFrameBuffer2= static_cast(this->MaxValueFrameBuffer2); glDeleteTextures(1,&maxValueFrameBuffer2); this->MaxValueFrameBuffer2=0; } GLuint programShader=static_cast(this->ProgramShader); vtkgl::DeleteProgram(programShader); this->ProgramShader=0; GLuint fragmentComponentShader= static_cast(this->FragmentComponentShader); vtkgl::DeleteShader(fragmentComponentShader); GLuint fragmentShadeShader= static_cast(this->FragmentShadeShader); vtkgl::DeleteShader(fragmentShadeShader); GLuint scaleBiasProgramShader= static_cast(this->ScaleBiasProgramShader); if(scaleBiasProgramShader!=0) { vtkgl::DeleteProgram(scaleBiasProgramShader); this->ScaleBiasProgramShader=0; } this->LastParallelProjection= vtkMitkOpenGLGPUVolumeRayCastMapperProjectionNotInitialized; this->LastRayCastMethod= vtkMitkOpenGLGPUVolumeRayCastMapperMethodNotInitialized; this->LastCroppingMode= vtkMitkOpenGLGPUVolumeRayCastMapperCroppingNotInitialized; this->LastComponent= vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotInitialized; this->LastShade=vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotInitialized; this->OpenGLObjectsCreated=0; } if(this->NoiseTextureId!=0) { window->MakeCurrent(); GLuint noiseTextureObjects=static_cast(this->NoiseTextureId); glDeleteTextures(1,&noiseTextureObjects); this->NoiseTextureId=0; } if(this->ScalarsTextures!=0) { if(!this->ScalarsTextures->Map.empty()) { vtkstd::map::iterator it=this->ScalarsTextures->Map.begin(); while(it!=this->ScalarsTextures->Map.end()) { vtkKWScalarField *texture=(*it).second; delete texture; ++it; } this->ScalarsTextures->Map.clear(); } } if(this->MaskTextures!=0) { if(!this->MaskTextures->Map.empty()) { vtkstd::map::iterator it=this->MaskTextures->Map.begin(); while(it!=this->MaskTextures->Map.end()) { vtkKWMask *texture=(*it).second; delete texture; ++it; } this->MaskTextures->Map.clear(); } } if(this->RGBTable!=0) { delete this->RGBTable; this->RGBTable=0; } if(this->Mask1RGBTable!=0) { delete this->Mask1RGBTable; this->Mask1RGBTable=0; } if(this->Mask2RGBTable!=0) { delete this->Mask2RGBTable; this->Mask2RGBTable=0; } if(this->OpacityTables!=0) { delete this->OpacityTables; this->OpacityTables=0; } } //----------------------------------------------------------------------------- // Allocate memory on the GPU for the framebuffers according to the size of // the window or reallocate if the size has changed. Return true if // allocation succeeded. // \pre ren_exists: ren!=0 // \pre opengl_objects_created: this->OpenGLObjectsCreated // \post right_size: LastSize[]=window size. //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::AllocateFrameBuffers(vtkRenderer *ren) { assert("pre: ren_exists" && ren!=0); assert("pre: opengl_objects_created" && this->OpenGLObjectsCreated); int result=1; int size[2]; ren->GetTiledSize(&size[0],&size[1]); int sizeChanged=this->LastSize[0]!=size[0] || this->LastSize[1]!=size[1]; GLenum errorCode=glGetError(); // Need allocation? if(sizeChanged) { int i=0; GLenum errorCode=glGetError(); while(i NumberOfFrameBuffers && errorCode==GL_NO_ERROR) { glBindTexture(GL_TEXTURE_2D,static_cast(this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+i])); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Here we are assuming that GL_ARB_texture_non_power_of_two is available if(this->Supports_GL_ARB_texture_float) { glTexImage2D(GL_TEXTURE_2D,0,vtkgl::RGBA16F_ARB,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } else { glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA16,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } errorCode=glGetError(); ++i; } if(errorCode==GL_NO_ERROR) { // grabbed depth buffer glBindTexture(GL_TEXTURE_2D,static_cast(this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectDepthMap])); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri(GL_TEXTURE_2D, vtkgl::DEPTH_TEXTURE_MODE, GL_LUMINANCE); glTexImage2D(GL_TEXTURE_2D, 0, vtkgl::DEPTH_COMPONENT32, size[0],size[1], 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL ); // Set up the depth render buffer GLint savedFrameBuffer; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&savedFrameBuffer); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(this->FrameBufferObject)); this->BindFramebuffer(); vtkgl::BindRenderbufferEXT( vtkgl::RENDERBUFFER_EXT, static_cast(this->DepthRenderBufferObject)); vtkgl::RenderbufferStorageEXT(vtkgl::RENDERBUFFER_EXT, vtkgl::DEPTH_COMPONENT24,size[0],size[1]); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(savedFrameBuffer)); errorCode=glGetError(); if(errorCode==GL_NO_ERROR) { this->LastSize[0]=size[0]; this->LastSize[1]=size[1]; } } result=errorCode==GL_NO_ERROR; } int needNewMaxValueBuffer=this->MaxValueFrameBuffer==0 && (this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND); if(needNewMaxValueBuffer) { // blend mode changed and need max value buffer. // create and bind second color buffer (we use only the red component // to store the max scalar). We cant use a one component color buffer // because all color buffer have to have the same format. // max scalar frame buffer GLuint maxValueFrameBuffer; glGenTextures(1,&maxValueFrameBuffer); this->MaxValueFrameBuffer= static_cast(maxValueFrameBuffer); // Color buffers this->m_BindMax = true; // max scalar frame buffer2 GLuint maxValueFrameBuffer2; glGenTextures(1,&maxValueFrameBuffer2); glBindTexture(GL_TEXTURE_2D,maxValueFrameBuffer2); this->MaxValueFrameBuffer2= static_cast(maxValueFrameBuffer2); } else { if(this->MaxValueFrameBuffer!=0 && (this->BlendMode!=vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND && this->BlendMode!=vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND)) { // blend mode changed and does not need max value buffer anymore. GLint savedFrameBuffer; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&savedFrameBuffer); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(this->FrameBufferObject)); vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D,0,0); // not scalar buffer vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(savedFrameBuffer)); GLuint maxValueFrameBuffer= static_cast(this->MaxValueFrameBuffer); glDeleteTextures(1,&maxValueFrameBuffer); this->MaxValueFrameBuffer=0; m_BindMax = false; GLuint maxValueFrameBuffer2= static_cast(this->MaxValueFrameBuffer2); glDeleteTextures(1,&maxValueFrameBuffer2); this->MaxValueFrameBuffer2=0; } } if((this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) && (sizeChanged || needNewMaxValueBuffer)) { // max scalar frame buffer GLuint maxValueFrameBuffer=static_cast(this->MaxValueFrameBuffer); glBindTexture(GL_TEXTURE_2D,maxValueFrameBuffer); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Here we are assuming that GL_ARB_texture_non_power_of_two is available if(this->Supports_GL_ARB_texture_float) { glTexImage2D(GL_TEXTURE_2D,0,vtkgl::RGBA16F_ARB,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } else { glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA16,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } // max scalar frame buffer 2 GLuint maxValueFrameBuffer2=static_cast(this->MaxValueFrameBuffer2); glBindTexture(GL_TEXTURE_2D,maxValueFrameBuffer2); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Here we are assuming that GL_ARB_texture_non_power_of_two is available if(this->Supports_GL_ARB_texture_float) { glTexImage2D(GL_TEXTURE_2D,0,vtkgl::RGBA16F_ARB,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } else { glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA16,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } } PrintError("AllocateFrameBuffers"); return result; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::GetTextureFormat( vtkImageData *input, unsigned int *internalFormat, unsigned int *format, unsigned int *type, int *componentSize) { *internalFormat=0; *format=0; *type=0; *componentSize=0; vtkDataArray *scalars=this->GetScalars(input,this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, this->CellFlag); int scalarType=scalars->GetDataType(); int components=scalars->GetNumberOfComponents(); *componentSize=vtkAbstractArray::GetDataTypeSize(scalarType)*components; if(components==4) { // this is RGBA, unsigned char only *internalFormat=GL_RGBA16; *format=GL_RGBA; *type=GL_UNSIGNED_BYTE; } else { // components==1 switch(scalarType) { case VTK_FLOAT: if(this->Supports_GL_ARB_texture_float) { *internalFormat=vtkgl::INTENSITY16F_ARB; } else { *internalFormat=GL_INTENSITY16; } *format=GL_RED; *type=GL_FLOAT; break; case VTK_UNSIGNED_CHAR: *internalFormat=GL_INTENSITY8; *format=GL_RED; *type=GL_UNSIGNED_BYTE; break; case VTK_SIGNED_CHAR: *internalFormat=GL_INTENSITY8; *format=GL_RED; *type=GL_BYTE; break; case VTK_CHAR: // not supported assert("check: impossible case" && 0); break; case VTK_BIT: // not supported assert("check: impossible case" && 0); break; case VTK_ID_TYPE: // not supported assert("check: impossible case" && 0); break; case VTK_INT: *internalFormat=GL_INTENSITY16; *format=GL_RED; *type=GL_INT; break; case VTK_DOUBLE: case VTK___INT64: case VTK_LONG: case VTK_LONG_LONG: case VTK_UNSIGNED___INT64: case VTK_UNSIGNED_LONG: case VTK_UNSIGNED_LONG_LONG: if(this->Supports_GL_ARB_texture_float) { *internalFormat=vtkgl::INTENSITY16F_ARB; } else { *internalFormat=GL_INTENSITY16; } *format=GL_RED; *type=GL_FLOAT; break; case VTK_SHORT: *internalFormat=GL_INTENSITY16; *format=GL_RED; *type=GL_SHORT; break; case VTK_STRING: // not supported assert("check: impossible case" && 0); break; case VTK_UNSIGNED_SHORT: *internalFormat=GL_INTENSITY16; *format=GL_RED; *type=GL_UNSIGNED_SHORT; break; case VTK_UNSIGNED_INT: *internalFormat=GL_INTENSITY16; *format=GL_RED; *type=GL_UNSIGNED_INT; break; default: assert("check: impossible case" && 0); break; } } } //----------------------------------------------------------------------------- // Assuming the textureSize[3] is less of equal to the maximum size of an // OpenGL 3D texture, try to see if the texture can fit on the card. //----------------------------------------------------------------------------- bool vtkMitkOpenGLGPUVolumeRayCastMapper::TestLoadingScalar( unsigned int internalFormat, unsigned int format, unsigned int type, int textureSize[3], int componentSize) { // componentSize=vtkAbstractArray::GetDataTypeSize(scalarType)*input->GetNumberOfScalarComponents() bool result; vtkgl::TexImage3D(vtkgl::PROXY_TEXTURE_3D,0, static_cast(internalFormat), textureSize[0],textureSize[1],textureSize[2],0, format, type,0); GLint width; glGetTexLevelParameteriv(vtkgl::PROXY_TEXTURE_3D,0,GL_TEXTURE_WIDTH, &width); result=width!=0; if(result) { // so far, so good but some cards always succeed with a proxy texture // let's try to actually allocate.. vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,static_cast(internalFormat), textureSize[0], textureSize[1],textureSize[2],0, format, type,0); GLenum errorCode=glGetError(); result=errorCode!=GL_OUT_OF_MEMORY; if(result) { if(errorCode!=GL_NO_ERROR) { cout<<"after try to load the texture"; cout<<" ERROR (x"<(errorCode)); cout<(this->MaxMemoryInBytes)*this->MaxMemoryFraction; } } return result; } //----------------------------------------------------------------------------- // Load the scalar field (one or four component scalar field), cell or point // based for a given subextent of the whole extent (can be the whole extent) // as a 3D texture on the GPU. // Extents are expressed in point if the cell flag is false or in cells of // the cell flag is true. // It returns true if it succeeded, false if there is not enough memory on // the GPU. // If succeeded, it updates the LoadedExtent, LoadedBounds, LoadedCellFlag // and LoadedTime. It also succeed if the scalar field is already loaded // (ie since last load, input has not changed and cell flag has not changed // and requested texture extents are enclosed in the loaded extent). // \pre input_exists: input!=0 // \pre valid_point_extent: (this->CellFlag || // (textureExtent[0]CellFlag || // (textureExtent[0]<=textureExtent[1] && // textureExtent[2]<=textureExtent[3] && // textureExtent[4]<=textureExtent[5]))) //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::LoadScalarField(vtkImageData *input, vtkImageData *maskInput, int textureExtent[6], vtkVolume *volume) { assert("pre: input_exists" && input!=0); assert("pre: valid_point_extent" && (this->CellFlag || (textureExtent[0]CellFlag || (textureExtent[0]<=textureExtent[1] && textureExtent[2]<=textureExtent[3] && textureExtent[4]<=textureExtent[5]))); int result=1; // succeeded // make sure we rebind our texture object to texture0 even if we don't have // to load the data themselves because the binding might be changed by // another mapper between two rendering calls. vtkgl::ActiveTexture(vtkgl::TEXTURE0); // Find the texture. vtkstd::map::iterator it= this->ScalarsTextures->Map.find(input); vtkKWScalarField *texture; if(it==this->ScalarsTextures->Map.end()) { texture=new vtkKWScalarField; this->ScalarsTextures->Map[input]=texture; texture->SetSupports_GL_ARB_texture_float(this->Supports_GL_ARB_texture_float==1); } else { texture=(*it).second; } texture->Update(input,this->CellFlag,textureExtent,this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, volume->GetProperty()->GetInterpolationType() ==VTK_LINEAR_INTERPOLATION, this->TableRange, static_cast(static_cast(this->MaxMemoryInBytes)*this->MaxMemoryFraction)); result=texture->IsLoaded(); this->CurrentScalar=texture; // Mask if(maskInput!=0) { vtkgl::ActiveTexture(vtkgl::TEXTURE7); // Find the texture. vtkstd::map::iterator it2= this->MaskTextures->Map.find(maskInput); vtkKWMask *mask; if(it2==this->MaskTextures->Map.end()) { mask=new vtkKWMask; this->MaskTextures->Map[maskInput]=mask; } else { mask=(*it2).second; } mask->Update(maskInput,this->CellFlag,textureExtent,this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, static_cast(static_cast(this->MaxMemoryInBytes)*this->MaxMemoryFraction)); result=result && mask->IsLoaded(); this->CurrentMask=mask; vtkgl::ActiveTexture(vtkgl::TEXTURE0); } return result; } //----------------------------------------------------------------------------- // Allocate memory and load color table on the GPU or // reload it if the transfer function changed. // \pre vol_exists: vol!=0 // \pre valid_numberOfScalarComponents: numberOfScalarComponents==1 || numberOfScalarComponents==4 //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::UpdateColorTransferFunction( vtkVolume *vol, int numberOfScalarComponents) { assert("pre: vol_exists" && vol!=0); assert("pre: valid_numberOfScalarComponents" && (numberOfScalarComponents==1 || numberOfScalarComponents==4)); // Build the colormap in a 1D texture. // 1D RGB-texture=mapping from scalar values to color values // build the table if(numberOfScalarComponents==1) { vtkVolumeProperty *volumeProperty=vol->GetProperty(); vtkColorTransferFunction *colorTransferFunction=volumeProperty->GetRGBTransferFunction(0); vtkgl::ActiveTexture(vtkgl::TEXTURE1); this->RGBTable->Update( colorTransferFunction,this->TableRange, volumeProperty->GetInterpolationType()==VTK_LINEAR_INTERPOLATION); // Restore default vtkgl::ActiveTexture( vtkgl::TEXTURE0); } if(this->MaskInput!=0) { vtkVolumeProperty *volumeProperty=vol->GetProperty(); vtkColorTransferFunction *c=volumeProperty->GetRGBTransferFunction(1); vtkgl::ActiveTexture(vtkgl::TEXTURE8); this->Mask1RGBTable->Update(c,this->TableRange,false); c=volumeProperty->GetRGBTransferFunction(2); vtkgl::ActiveTexture(vtkgl::TEXTURE9); this->Mask2RGBTable->Update(c,this->TableRange,false); // Restore default vtkgl::ActiveTexture( vtkgl::TEXTURE0); } return 1; } //----------------------------------------------------------------------------- // Allocate memory and load opacity table on the GPU or // reload it if the transfert function changed. // \pre vol_exists: vol!=0 // \pre valid_numberOfScalarComponents: numberOfScalarComponents==1 || numberOfScalarComponents==4 //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::UpdateOpacityTransferFunction( vtkVolume *vol, int numberOfScalarComponents, unsigned int level) { assert("pre: vol_exists" && vol!=0); assert("pre: valid_numberOfScalarComponents" && (numberOfScalarComponents==1 || numberOfScalarComponents==4)); (void)numberOfScalarComponents; // remove warning in release mode. vtkVolumeProperty *volumeProperty=vol->GetProperty(); vtkPiecewiseFunction *scalarOpacity=volumeProperty->GetScalarOpacity(); vtkgl::ActiveTexture( vtkgl::TEXTURE2); //stay here this->OpacityTables->Vector[level].Update( scalarOpacity,this->BlendMode, this->ActualSampleDistance, this->TableRange, volumeProperty->GetScalarOpacityUnitDistance(0), volumeProperty->GetInterpolationType()==VTK_LINEAR_INTERPOLATION); // Restore default active texture vtkgl::ActiveTexture( vtkgl::TEXTURE0); return 1; } //----------------------------------------------------------------------------- // Prepare rendering in the offscreen framebuffer. // \pre ren_exists: ren!=0 // \pre vol_exists: vol!=0 //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::SetupRender(vtkRenderer *ren, vtkVolume *vol) { assert("pre: ren_exists" && ren!=0); assert("pre: vol_exists" && vol!=0); double aspect[2]; int lowerLeft[2]; int usize, vsize; ren->GetTiledSizeAndOrigin(&usize,&vsize,lowerLeft,lowerLeft+1); usize = static_cast(usize*this->ReductionFactor); vsize = static_cast(vsize*this->ReductionFactor); this->ReducedSize[0]=usize; this->ReducedSize[1]=vsize; // the FBO has the size of the renderer (not the renderwindow), // we always starts at 0,0. glViewport(0,0, usize, vsize); glEnable( GL_SCISSOR_TEST ); // scissor on the FBO, on the reduced part. glScissor(0,0, usize, vsize); glClearColor(0.0, 0.0, 0.0, 0.0); // maxvalue is 1 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ren->ComputeAspect(); ren->GetAspect(aspect); double aspect2[2]; ren->vtkViewport::ComputeAspect(); ren->vtkViewport::GetAspect(aspect2); double aspectModification = aspect[0]*aspect2[1]/(aspect[1]*aspect2[0]); vtkCamera *cam = ren->GetActiveCamera(); glMatrixMode( GL_PROJECTION); if(usize && vsize) { this->TempMatrix[0]->DeepCopy(cam->GetProjectionTransformMatrix( aspectModification*usize/vsize, -1,1)); this->TempMatrix[0]->Transpose(); glLoadMatrixd(this->TempMatrix[0]->Element[0]); } else { glLoadIdentity(); } // push the model view matrix onto the stack, make sure we // adjust the mode first glMatrixMode(GL_MODELVIEW); glPushMatrix(); this->TempMatrix[0]->DeepCopy(vol->GetMatrix()); this->TempMatrix[0]->Transpose(); // insert camera view transformation glMultMatrixd(this->TempMatrix[0]->Element[0]); glShadeModel(GL_SMOOTH); glDisable( GL_LIGHTING); glEnable (GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); // very important, otherwise the first image looks dark. this->PrintError("SetupRender"); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::DebugDisplayBox(vtkPolyData *box) { vtkPoints *points=box->GetPoints(); vtkCellArray *polys=box->GetPolys(); cout<<"npts="<GetNumberOfPoints()<GetNumberOfPoints()) { double coords[3]; points->GetPoint(pointId,coords); cout<<"pointId="<GetMatrix( this->InvVolumeMatrix ); this->InvVolumeMatrix->Invert(); if(this->BoxSource==0) { this->BoxSource=vtkTessellatedBoxSource::New(); } this->BoxSource->SetBounds(worldBounds); this->BoxSource->SetLevel(0); this->BoxSource->QuadsOn(); if(this->Planes==0) { this->Planes=vtkPlaneCollection::New(); } this->Planes->RemoveAllItems(); vtkCamera *cam = ren->GetActiveCamera(); double camWorldRange[2]; double camWorldPos[4]; double camFocalWorldPoint[4]; double camWorldDirection[3]; double range[2]; double camPos[4]; double focalPoint[4]; double direction[3]; cam->GetPosition(camWorldPos); camWorldPos[3] = 1.0; this->InvVolumeMatrix->MultiplyPoint( camWorldPos, camPos ); if ( camPos[3] ) { camPos[0] /= camPos[3]; camPos[1] /= camPos[3]; camPos[2] /= camPos[3]; } cam->GetFocalPoint(camFocalWorldPoint); camFocalWorldPoint[3]=1.0; this->InvVolumeMatrix->MultiplyPoint( camFocalWorldPoint,focalPoint ); if ( focalPoint[3] ) { focalPoint[0] /= focalPoint[3]; focalPoint[1] /= focalPoint[3]; focalPoint[2] /= focalPoint[3]; } // Compute the normalized view direction direction[0] = focalPoint[0] - camPos[0]; direction[1] = focalPoint[1] - camPos[1]; direction[2] = focalPoint[2] - camPos[2]; vtkMath::Normalize(direction); // The range (near/far) must also be transformed // into the local coordinate system. camWorldDirection[0] = camFocalWorldPoint[0] - camWorldPos[0]; camWorldDirection[1] = camFocalWorldPoint[1] - camWorldPos[1]; camWorldDirection[2] = camFocalWorldPoint[2] - camWorldPos[2]; vtkMath::Normalize(camWorldDirection); double camNearWorldPoint[4]; double camFarWorldPoint[4]; double camNearPoint[4]; double camFarPoint[4]; cam->GetClippingRange(camWorldRange); + MITK_INFO << "vtkMiktOpenGLGPUVolumeRayCastMapper.cpp"; camNearWorldPoint[0] = camWorldPos[0] + camWorldRange[0]*camWorldDirection[0]; camNearWorldPoint[1] = camWorldPos[1] + camWorldRange[0]*camWorldDirection[1]; camNearWorldPoint[2] = camWorldPos[2] + camWorldRange[0]*camWorldDirection[2]; camNearWorldPoint[3] = 1.; camFarWorldPoint[0] = camWorldPos[0] + camWorldRange[1]*camWorldDirection[0]; camFarWorldPoint[1] = camWorldPos[1] + camWorldRange[1]*camWorldDirection[1]; camFarWorldPoint[2] = camWorldPos[2] + camWorldRange[1]*camWorldDirection[2]; camFarWorldPoint[3] = 1.; this->InvVolumeMatrix->MultiplyPoint( camNearWorldPoint, camNearPoint ); if (camNearPoint[3]) { camNearPoint[0] /= camNearPoint[3]; camNearPoint[1] /= camNearPoint[3]; camNearPoint[2] /= camNearPoint[3]; } this->InvVolumeMatrix->MultiplyPoint( camFarWorldPoint, camFarPoint ); if (camFarPoint[3]) { camFarPoint[0] /= camFarPoint[3]; camFarPoint[1] /= camFarPoint[3]; camFarPoint[2] /= camFarPoint[3]; } range[0] = sqrt(vtkMath::Distance2BetweenPoints(camNearPoint, camPos)); range[1] = sqrt(vtkMath::Distance2BetweenPoints(camFarPoint, camPos)); //double nearPoint[3], farPoint[3]; double dist = range[1] - range[0]; range[0] += dist / (2<<16); range[1] -= dist / (2<<16); if(this->NearPlane==0) { this->NearPlane= vtkPlane::New(); } //this->NearPlane->SetOrigin( nearPoint ); this->NearPlane->SetOrigin( camNearPoint ); this->NearPlane->SetNormal( direction ); this->Planes->AddItem(this->NearPlane); if ( this->ClippingPlanes ) { this->ClippingPlanes->InitTraversal(); vtkPlane *plane; while ( (plane = this->ClippingPlanes->GetNextItem()) ) { // Planes are in world coordinates, we need to // convert them in local coordinates double planeOrigin[4], planeNormal[4], planeP1[4]; plane->GetOrigin(planeOrigin); planeOrigin[3] = 1.; plane->GetNormal(planeNormal); planeP1[0] = planeOrigin[0] + planeNormal[0]; planeP1[1] = planeOrigin[1] + planeNormal[1]; planeP1[2] = planeOrigin[2] + planeNormal[2]; planeP1[3] = 1.; this->InvVolumeMatrix->MultiplyPoint(planeOrigin, planeOrigin); this->InvVolumeMatrix->MultiplyPoint(planeP1, planeP1); if( planeOrigin[3]) { planeOrigin[0] /= planeOrigin[3]; planeOrigin[1] /= planeOrigin[3]; planeOrigin[2] /= planeOrigin[3]; } if( planeP1[3]) { planeP1[0] /= planeP1[3]; planeP1[1] /= planeP1[3]; planeP1[2] /= planeP1[3]; } planeNormal[0] = planeP1[0] - planeOrigin[0]; planeNormal[1] = planeP1[1] - planeOrigin[1]; planeNormal[2] = planeP1[2] - planeOrigin[2]; vtkMath::Normalize(planeNormal); vtkPlane* localPlane = vtkPlane::New(); localPlane->SetOrigin(planeOrigin); localPlane->SetNormal(planeNormal); this->Planes->AddItem(localPlane); localPlane->Delete(); } } if(this->Clip==0) { this->Clip=vtkClipConvexPolyData::New(); this->Clip->SetInputConnection(this->BoxSource->GetOutputPort()); this->Clip->SetPlanes( this->Planes ); } this->Clip->Update(); if(this->Densify==0) { this->Densify=vtkDensifyPolyData::New(); this->Densify->SetInputConnection(this->Clip->GetOutputPort()); this->Densify->SetNumberOfSubdivisions(2); } this->Densify->Update(); this->ClippedBoundingBox = this->Densify->GetOutput(); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::RenderClippedBoundingBox( int tcoordFlag, size_t currentBlock, size_t numberOfBlocks, vtkRenderWindow *renWin ) { assert("pre: valid_currentBlock" && currentBlockClippedBoundingBox->GetPoints(); vtkCellArray *polys = this->ClippedBoundingBox->GetPolys(); vtkIdType npts; vtkIdType *pts; vtkIdType i, j; double center[3] = {0,0,0}; double min[3] = {VTK_DOUBLE_MAX, VTK_DOUBLE_MAX, VTK_DOUBLE_MAX}; double max[3] = {VTK_DOUBLE_MIN, VTK_DOUBLE_MIN, VTK_DOUBLE_MIN}; // First compute center point npts = points->GetNumberOfPoints(); for ( i = 0; i < npts; i++ ) { double pt[3]; points->GetPoint( i, pt ); for ( j = 0; j < 3; j++ ) { min[j] = (pt[j]max[j])?(pt[j]):(max[j]); } } center[0] = 0.5*(min[0]+max[0]); center[1] = 0.5*(min[1]+max[1]); center[2] = 0.5*(min[2]+max[2]); double *loadedBounds=0; vtkIdType *loadedExtent=0; if ( tcoordFlag ) { loadedBounds=this->CurrentScalar->GetLoadedBounds(); loadedExtent=this->CurrentScalar->GetLoadedExtent(); } double *spacing=this->GetInput()->GetSpacing(); double spacingSign[3]; i=0; while(i<3) { if(spacing[i]<0) { spacingSign[i]=-1.0; } else { spacingSign[i]=1.0; } ++i; } // make it double for the ratio of the progress. int polyId=0; double polyCount=static_cast(polys->GetNumberOfCells()); polys->InitTraversal(); int abort=0; while ( !abort && polys->GetNextCell(npts, pts) ) { vtkIdType start, end, inc; // Need to have at least a triangle if ( npts > 2 ) { // Check the cross product of the first two // vectors dotted with the vector from the // center to the second point. Is it positive or // negative? double p1[3], p2[3], p3[3]; double v1[3], v2[3], v3[3], v4[3]; points->GetPoint(pts[0], p1 ); points->GetPoint(pts[1], p2 ); points->GetPoint(pts[2], p3 ); v1[0] = p2[0] - p1[0]; v1[1] = p2[1] - p1[1]; v1[2] = p2[2] - p1[2]; v2[0] = p2[0] - p3[0]; v2[1] = p2[1] - p3[1]; v2[2] = p2[2] - p3[2]; vtkMath::Cross( v1, v2, v3 ); vtkMath::Normalize(v3); v4[0] = p2[0] - center[0]; v4[1] = p2[1] - center[1]; v4[2] = p2[2] - center[2]; vtkMath::Normalize(v4); double dot = vtkMath::Dot( v3, v4 ); if (( dot < 0) && this->PreserveOrientation) { start = 0; end = npts; inc = 1; } else { start = npts-1; end = -1; inc = -1; } glBegin( GL_TRIANGLE_FAN ); // GL_POLYGON -> GL_TRIANGLE_FAN double vert[3]; double tcoord[3]; for ( i = start; i != end; i += inc ) { points->GetPoint(pts[i], vert); if ( tcoordFlag ) { for ( j = 0; j < 3; j++ ) { // loaded bounds take both cell data and point date cases into // account if(this->CellFlag) // texcoords between 0 and 1. More complex // depends on the loaded texture { tcoord[j] = spacingSign[j]*(vert[j] - loadedBounds[j*2]) / (loadedBounds[j*2+1] - loadedBounds[j*2]); } else // texcoords between 1/2N and 1-1/2N. { double tmp; // between 0 and 1 tmp = spacingSign[j]*(vert[j] - loadedBounds[j*2]) / (loadedBounds[j*2+1] - loadedBounds[j*2]); double delta=static_cast( loadedExtent[j*2+1]-loadedExtent[j*2]+1); tcoord[j]=(tmp*(delta-1)+0.5)/delta; } } vtkgl::MultiTexCoord3dv(vtkgl::TEXTURE0, tcoord); } glVertex3dv(vert); } glEnd(); } if(tcoordFlag) { // otherwise, we are rendering back face to initialize the zbuffer. if (!this->GeneratingCanonicalView && this->ReportProgress) { glFinish(); // Only invoke an event at most one every second. double currentTime=vtkTimerLog::GetUniversalTime(); if(currentTime - this->LastProgressEventTime > 1.0) { double progress=(static_cast(currentBlock)+polyId/polyCount)/ static_cast(numberOfBlocks); this->InvokeEvent(vtkCommand::VolumeMapperRenderProgressEvent, &progress); renWin->MakeCurrent(); this->LastProgressEventTime = currentTime; } } abort=renWin->CheckAbortStatus(); } ++polyId; } return abort; } void vtkMitkOpenGLGPUVolumeRayCastMapper::CopyFBOToTexture() { // in OpenGL copy texture to texture does not exist but // framebuffer to texture exists (and our FB is an FBO). // we have to copy and not just to switch color textures because the // colorbuffer has to accumulate color or values step after step. // Switching would not work because two different steps can draw different // polygons that don't overlap vtkgl::ActiveTexture(vtkgl::TEXTURE4); glBindTexture( GL_TEXTURE_2D, this->TextureObjects[ vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+1]); glReadBuffer(vtkgl::COLOR_ATTACHMENT0_EXT); glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,this->ReducedSize[0], this->ReducedSize[1]); if(this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) { vtkgl::ActiveTexture(vtkgl::TEXTURE5); glBindTexture(GL_TEXTURE_2D,this->MaxValueFrameBuffer2); glReadBuffer(vtkgl::COLOR_ATTACHMENT0_EXT+1); glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,this->ReducedSize[0], this->ReducedSize[1]); } vtkgl::ActiveTexture(vtkgl::TEXTURE0); } //----------------------------------------------------------------------------- // Restore OpenGL state after rendering of the dataset. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::CleanupRender() { glPopMatrix(); glDisable(GL_CULL_FACE); } //----------------------------------------------------------------------------- // Build the fragment shader program that scale and bias a texture // for window/level purpose. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::BuildScaleBiasProgram() { if(this->ScaleBiasProgramShader==0) { GLuint programShader; GLuint fragmentShader; programShader=vtkgl::CreateProgram(); fragmentShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::AttachShader(programShader,fragmentShader); vtkgl::DeleteShader(fragmentShader); // reference counting vtkgl::ShaderSource( fragmentShader,1, const_cast(&vtkMitkGPUVolumeRayCastMapper_ScaleBiasFS),0); vtkgl::CompileShader(fragmentShader); this->CheckCompilation(static_cast(fragmentShader)); vtkgl::LinkProgram(programShader); this->CheckLinkage(static_cast(programShader)); this->ScaleBiasProgramShader=static_cast(programShader); this->UFrameBufferTexture= static_cast(vtkgl::GetUniformLocation(programShader, "frameBufferTexture")); this->UScale=static_cast(vtkgl::GetUniformLocation(programShader, "scale")); this->UBias=static_cast(vtkgl::GetUniformLocation(programShader, "bias")); } } //----------------------------------------------------------------------------- // Render the offscreen buffer to the screen. // \pre ren_exists: ren!=0 //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::RenderTextureToScreen(vtkRenderer *ren) { assert("pre: ren_exists" && ren!=0); if ( this->GeneratingCanonicalView ) { // We just need to copy of the data, not render it glBindTexture(GL_TEXTURE_2D, this->TextureObjects[ vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); glPixelStorei( GL_PACK_ALIGNMENT, 1 ); unsigned char *outPtr = static_cast(this->CanonicalViewImageData->GetScalarPointer()); glGetTexImage( GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, outPtr ); return; } int lowerLeft[2]; int usize, vsize; ren->GetTiledSizeAndOrigin(&usize,&vsize,lowerLeft,lowerLeft+1); glViewport(lowerLeft[0],lowerLeft[1], usize, vsize); glEnable( GL_SCISSOR_TEST ); glScissor(lowerLeft[0],lowerLeft[1], usize, vsize); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, usize, 0.0, vsize, -1.0, 1.0 ); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glBindTexture(GL_TEXTURE_2D, this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glEnable(GL_BLEND); glBlendFunc( GL_ONE,GL_ONE_MINUS_SRC_ALPHA); // As we use replace mode, we don't need to set the color value. glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); glDisable(GL_DEPTH_TEST); double xOffset = 1.0 / usize; double yOffset = 1.0 / vsize; glDepthMask(GL_FALSE); double scale=1.0/this->FinalColorWindow; double bias=0.5-this->FinalColorLevel/this->FinalColorWindow; if(scale!=1.0 || bias!=0.0) { this->BuildScaleBiasProgram(); vtkgl::UseProgram(this->ScaleBiasProgramShader); if(this->UFrameBufferTexture!=-1) { vtkgl::Uniform1i(this->UFrameBufferTexture,0); } else { vtkErrorMacro(<<"uFrameBufferTexture is not a uniform variable."); } if(this->UScale!=-1) { vtkgl::Uniform1f(this->UScale,static_cast(scale)); } else { vtkErrorMacro(<<"uScale is not a uniform variable."); } if(this->UBias!=-1) { vtkgl::Uniform1f(this->UBias,static_cast(bias)); } else { vtkErrorMacro(<<"uBias is not a uniform variable."); } } else { glEnable(GL_TEXTURE_2D); // fixed pipeline } glBegin(GL_QUADS); glTexCoord2f(static_cast(xOffset),static_cast(yOffset)); glVertex2f(0.0,0.0); glTexCoord2f(static_cast(this->ReductionFactor-xOffset), static_cast(yOffset)); glVertex2f(static_cast(usize),0.0); glTexCoord2f(static_cast(this->ReductionFactor-xOffset), static_cast(this->ReductionFactor-yOffset)); glVertex2f(static_cast(usize),static_cast(vsize)); glTexCoord2f(static_cast(xOffset), static_cast(this->ReductionFactor-yOffset)); glVertex2f(0.0,static_cast(vsize)); glEnd(); // Restore the default mode. Used in overlay. glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); if(scale!=1.0 || bias!=0.0) { vtkgl::UseProgram(0); } else { glDisable(GL_TEXTURE_2D); } glDepthMask(GL_TRUE); glDisable(GL_BLEND); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } //----------------------------------------------------------------------------- // Update the reduction factor of the render viewport (this->ReductionFactor) // according to the time spent in seconds to render the previous frame // (this->TimeToDraw) and a time in seconds allocated to render the next // frame (allocatedTime). // \pre valid_current_reduction_range: this->ReductionFactor>0.0 && this->ReductionFactor<=1.0 // \pre positive_TimeToDraw: this->TimeToDraw>=0.0 // \pre positive_time: allocatedTime>0.0 // \post valid_new_reduction_range: this->ReductionFactor>0.0 && this->ReductionFactor<=1.0 //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::ComputeReductionFactor( double allocatedTime) { assert("pre: valid_current_reduction_range" && this->ReductionFactor>0.0 && this->ReductionFactor<=1.0); assert("pre: positive_TimeToDraw" && this->TimeToDraw>=0.0); assert("pre: positive_time" && allocatedTime>0.0); if ( this->GeneratingCanonicalView ) { this->ReductionFactor = 1.0; return; } if ( !this->AutoAdjustSampleDistances ) { this->ReductionFactor = 1.0 / this->ImageSampleDistance; return; } if ( this->TimeToDraw ) { double oldFactor = this->ReductionFactor; double timeToDraw; if (allocatedTime < 1.0) { timeToDraw = this->SmallTimeToDraw; if ( timeToDraw == 0.0 ) { timeToDraw = this->BigTimeToDraw/3.0; } } else { timeToDraw = this->BigTimeToDraw; } if ( timeToDraw == 0.0 ) { timeToDraw = 10.0; } double fullTime = timeToDraw / this->ReductionFactor; double newFactor = allocatedTime / fullTime; if ( oldFactor == 1.0 || newFactor / oldFactor > 1.3 || newFactor / oldFactor < .95 ) { this->ReductionFactor = (newFactor+oldFactor)/2.0; this->ReductionFactor = (this->ReductionFactor > 5.0)?(1.00):(this->ReductionFactor); this->ReductionFactor = (this->ReductionFactor > 1.0)?(0.99):(this->ReductionFactor); this->ReductionFactor = (this->ReductionFactor < 0.1)?(0.10):(this->ReductionFactor); if ( 1.0/this->ReductionFactor > this->MaximumImageSampleDistance ) { this->ReductionFactor = 1.0 / this->MaximumImageSampleDistance; } if ( 1.0/this->ReductionFactor < this->MinimumImageSampleDistance ) { this->ReductionFactor = 1.0 / this->MinimumImageSampleDistance; } } } else { this->ReductionFactor = 1.0; } assert("post: valid_new_reduction_range" && this->ReductionFactor>0.0 && this->ReductionFactor<=1.0); } //----------------------------------------------------------------------------- // Rendering initialization including making the context current, loading // necessary extensions, allocating frame buffers, updating transfer function, // computing clipping regions, and building the fragment shader. // // Pre-conditions: // - ren != NULL // - vol != NULL // - ren->GetRenderWindow() != NULL // - 1 <= numberOfScalarComponents <= 4 // - numberOfLevels >= 1 //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::PreRender(vtkRenderer *ren, vtkVolume *vol, double datasetBounds[6], double scalarRange[2], int numberOfScalarComponents, unsigned int numberOfLevels) { // make sure our window is the current OpenGL context. ren->GetRenderWindow()->MakeCurrent(); // If we haven't already succeeded in loading the extensions, // try to load them if(!this->LoadExtensionsSucceeded) { this->LoadExtensions(ren->GetRenderWindow()); } // If we can't load the necessary extensions, provide // feedback on why it failed. if(!this->LoadExtensionsSucceeded) { vtkErrorMacro( "Rendering failed because the following OpenGL extensions " "are required but not supported: " << (this->UnsupportedRequiredExtensions->Stream.str()).c_str()); return; } // Create the OpenGL object that we need this->CreateOpenGLObjects(); // Compute the reduction factor that may be necessary to get // the interactive rendering rate that we want this->ComputeReductionFactor(vol->GetAllocatedRenderTime()); // Allocate the frame buffers if(!this->AllocateFrameBuffers(ren)) { vtkErrorMacro("Not enough GPU memory to create a framebuffer."); return; } // Save the scalar range - this is what we will use for the range // of the transfer functions this->TableRange[0]=scalarRange[0]; this->TableRange[1]=scalarRange[1]; if(this->RGBTable==0) { this->RGBTable=new vtkRGBTable; } if(this->MaskInput!=0) { if(this->Mask1RGBTable==0) { this->Mask1RGBTable=new vtkRGBTable; } if(this->Mask2RGBTable==0) { this->Mask2RGBTable=new vtkRGBTable; } } // Update the color transfer function this->UpdateColorTransferFunction(vol,numberOfScalarComponents); // Update the noise texture that will be used to jitter rays to // reduce alliasing artifacts this->UpdateNoiseTexture(); // We are going to change the blending mode and blending function - // so lets push here so we can pop later glPushAttrib(GL_COLOR_BUFFER_BIT); // If this is the canonical view - we don't want to intermix so we'll just // start by clearing the z buffer. if ( this->GeneratingCanonicalView ) { glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } // See if the volume transform is orientation-preserving vtkMatrix4x4 *m=vol->GetMatrix(); double det=vtkMath::Determinant3x3( m->GetElement(0,0),m->GetElement(0,1),m->GetElement(0,2), m->GetElement(1,0),m->GetElement(1,1),m->GetElement(1,2), m->GetElement(2,0),m->GetElement(2,1),m->GetElement(2,2)); this->PreserveOrientation=det>0; // If we have clipping planes, render the back faces of the clipped // bounding box of the whole dataset to set the zbuffer. if(this->ClippingPlanes && this->ClippingPlanes->GetNumberOfItems()!=0) { // push the model view matrix onto the stack, make sure we // adjust the mode first glMatrixMode(GL_MODELVIEW); glPushMatrix(); this->TempMatrix[0]->DeepCopy(vol->GetMatrix()); this->TempMatrix[0]->Transpose(); glMultMatrixd(this->TempMatrix[0]->Element[0]); this->ClipBoundingBox(ren,datasetBounds,vol); glEnable (GL_CULL_FACE); glCullFace (GL_FRONT); glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE); this->RenderClippedBoundingBox(0,0,1,ren->GetRenderWindow()); glDisable (GL_CULL_FACE); glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE); //glMatrixMode(GL_MODELVIEW); glPopMatrix(); } // Check if everything is OK this->CheckFrameBufferStatus(); // Intermixed geometry: Grab the depth buffer into a texture int size[2]; int lowerLeft[2]; ren->GetTiledSizeAndOrigin(size,size+1,lowerLeft,lowerLeft+1); vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glBindTexture(GL_TEXTURE_2D, static_cast( this->TextureObjects[ vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectDepthMap])); glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,lowerLeft[0],lowerLeft[1],size[0], size[1]); vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); int parallelProjection=ren->GetActiveCamera()->GetParallelProjection(); // initialize variables to prevent compiler warnings. int rayCastMethod=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP; int shadeMethod=vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotUsed; int componentMethod=vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotUsed; switch(this->BlendMode) { case vtkVolumeMapper::COMPOSITE_BLEND: switch(numberOfScalarComponents) { case 1: componentMethod=vtkMitkOpenGLGPUVolumeRayCastMapperComponentOne; break; case 4: componentMethod=vtkMitkOpenGLGPUVolumeRayCastMapperComponentFour; break; default: assert("check: impossible case" && false); break; } if(this->MaskInput!=0) { rayCastMethod= vtkMitkOpenGLGPUVolumeRayCastMapperMethodCompositeMask; } else { //cout<<"program is composite+shade"<GetProperty()->GetShade() ) { shadeMethod=vtkMitkOpenGLGPUVolumeRayCastMapperShadeYes; assert("check: only_1_component_todo" && numberOfScalarComponents==1); } else { shadeMethod=vtkMitkOpenGLGPUVolumeRayCastMapperShadeNo; //cout<<"program is composite"<ComputeNumberOfCroppingRegions(); // TODO AMR vs single block if(this->AMRMode) { NumberOfCroppingRegions=2; // >1, means use do compositing between blocks } this->BuildProgram(parallelProjection,rayCastMethod,shadeMethod, componentMethod); this->CheckLinkage(this->ProgramShader); vtkgl::UseProgram(this->ProgramShader); // for active texture 0, dataset if(numberOfScalarComponents==1) { // colortable vtkgl::ActiveTexture(vtkgl::TEXTURE1); this->RGBTable->Bind(); if(this->MaskInput!=0) { vtkgl::ActiveTexture(vtkgl::TEXTURE8); this->Mask1RGBTable->Bind(); vtkgl::ActiveTexture(vtkgl::TEXTURE9); this->Mask2RGBTable->Bind(); } } GLint uDataSetTexture; uDataSetTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"dataSetTexture"); if(uDataSetTexture!=-1) { vtkgl::Uniform1i(uDataSetTexture,0); } else { vtkErrorMacro(<<"dataSetTexture is not a uniform variable."); } if ( this->MaskInput) { // Make the mask texture available on texture unit 7 GLint uMaskTexture; uMaskTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"maskTexture"); if(uMaskTexture!=-1) { vtkgl::Uniform1i(uMaskTexture,7); } else { vtkErrorMacro(<<"maskTexture is not a uniform variable."); } } if(numberOfScalarComponents==1) { GLint uColorTexture; uColorTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"colorTexture"); if(uColorTexture!=-1) { vtkgl::Uniform1i(uColorTexture,1); } else { vtkErrorMacro(<<"colorTexture is not a uniform variable."); } if(this->MaskInput!=0) { GLint uMask1ColorTexture; uMask1ColorTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"mask1ColorTexture"); if(uMask1ColorTexture!=-1) { vtkgl::Uniform1i(uMask1ColorTexture,8); } else { vtkErrorMacro(<<"mask1ColorTexture is not a uniform variable."); } GLint uMask2ColorTexture; uMask2ColorTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"mask2ColorTexture"); if(uMask2ColorTexture!=-1) { vtkgl::Uniform1i(uMask2ColorTexture,9); } else { vtkErrorMacro(<<"mask2ColorTexture is not a uniform variable."); } GLint uMaskBlendFactor; uMaskBlendFactor=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"maskBlendFactor"); if(uMaskBlendFactor!=-1) { vtkgl::Uniform1f(uMaskBlendFactor,this->MaskBlendFactor); } else { vtkErrorMacro(<<"maskBlendFactor is not a uniform variable."); } } } GLint uOpacityTexture; uOpacityTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"opacityTexture"); if(uOpacityTexture!=-1) { vtkgl::Uniform1i(uOpacityTexture,2); } else { vtkErrorMacro(<<"opacityTexture is not a uniform variable."); } // depthtexture vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glBindTexture(GL_TEXTURE_2D,static_cast(this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectDepthMap])); GLint uDepthTexture; uDepthTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"depthTexture"); if(uDepthTexture!=-1) { vtkgl::Uniform1i(uDepthTexture,3); } else { vtkErrorMacro(<<"depthTexture is not a uniform variable."); } // noise texture vtkgl::ActiveTexture( vtkgl::TEXTURE6 ); glBindTexture(GL_TEXTURE_2D,static_cast(this->NoiseTextureId)); GLint uNoiseTexture; uNoiseTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"noiseTexture"); if(uNoiseTexture!=-1) { vtkgl::Uniform1i(uNoiseTexture,6); } else { vtkErrorMacro(<<"noiseTexture is not a uniform variable."); } this->CheckFrameBufferStatus(); if(this->NumberOfCroppingRegions>1) { // framebuffer texture if(rayCastMethod!=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP && rayCastMethod!=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMinIP) { vtkgl::ActiveTexture( vtkgl::TEXTURE4 ); glBindTexture(GL_TEXTURE_2D,static_cast(this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront])); GLint uFrameBufferTexture; uFrameBufferTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"frameBufferTexture"); this->PrintError("framebuffertexture 1"); if(uFrameBufferTexture!=-1) { vtkgl::Uniform1i(uFrameBufferTexture,4); } else { vtkErrorMacro(<<"frameBufferTexture is not a uniform variable."); } this->PrintError("framebuffertexture 2"); } this->CheckFrameBufferStatus(); // max scalar value framebuffer texture if(this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) { vtkgl::ActiveTexture( vtkgl::TEXTURE5 ); glBindTexture(GL_TEXTURE_2D,static_cast(this->MaxValueFrameBuffer2)); GLint uScalarBufferTexture; uScalarBufferTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"scalarBufferTexture"); this->PrintError("scalarbuffertexture 1"); if(uScalarBufferTexture!=-1) { vtkgl::Uniform1i(uScalarBufferTexture,5); } else { vtkErrorMacro(<<"scalarBufferTexture is not a uniform variable."); } this->PrintError("scalarbuffertexture 2"); } } this->CheckFrameBufferStatus(); GLint uWindowLowerLeftCorner; uWindowLowerLeftCorner=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"windowLowerLeftCorner"); if(uWindowLowerLeftCorner!=-1) { vtkgl::Uniform2f(uWindowLowerLeftCorner,static_cast(lowerLeft[0]), static_cast(lowerLeft[1])); } else { vtkErrorMacro(<<"windowLowerLeftCorner is not a uniform variable."); } GLint uInvOriginalWindowSize; uInvOriginalWindowSize=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"invOriginalWindowSize"); if(uInvOriginalWindowSize!=-1) { vtkgl::Uniform2f(uInvOriginalWindowSize, static_cast(1.0/size[0]), static_cast(1.0/size[1])); } else { // yes it is not error. It is only actually used when there is some // complex cropping (this->NumberOfCroppingRegions>1). Some GLSL compilers // may remove the uniform variable for optimization when it is not used. vtkDebugMacro( <<"invOriginalWindowSize is not an active uniform variable."); } size[0] = static_cast(size[0]*this->ReductionFactor); size[1] = static_cast(size[1]*this->ReductionFactor); GLint uInvWindowSize; uInvWindowSize=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"invWindowSize"); if(uInvWindowSize!=-1) { vtkgl::Uniform2f(uInvWindowSize,static_cast(1.0/size[0]), static_cast(1.0/size[1])); } else { vtkErrorMacro(<<"invWindowSize is not a uniform variable."); } this->PrintError("after uniforms for textures"); this->CheckFrameBufferStatus(); GLint savedFrameBuffer; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&savedFrameBuffer); this->SavedFrameBuffer=static_cast(savedFrameBuffer); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(this->FrameBufferObject)); this->BindFramebuffer(); GLenum buffer[4]; buffer[0] = vtkgl::COLOR_ATTACHMENT0_EXT; if(this->NumberOfCroppingRegions>1 && this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND) { // max scalar frame buffer buffer[1] = vtkgl::COLOR_ATTACHMENT1_EXT; } else { buffer[1] = GL_NONE; } vtkgl::DrawBuffers(2,buffer); this->CheckFrameBufferStatus(); // Use by the composite+shade program double shininess=vol->GetProperty()->GetSpecularPower(); if(shininess>128.0) { shininess=128.0; // upper limit for the OpenGL shininess. } glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,static_cast(shininess)); glDisable(GL_COLOR_MATERIAL); // other mapper may have enable that. GLfloat values[4]; values[3]=1.0; values[0]=0.0; values[1]=values[0]; values[2]=values[0]; glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,values); values[0]=static_cast(vol->GetProperty()->GetAmbient()); values[1]=values[0]; values[2]=values[0]; glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,values); values[0]=static_cast(vol->GetProperty()->GetDiffuse()); values[1]=values[0]; values[2]=values[0]; glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,values); values[0]=static_cast(vol->GetProperty()->GetSpecular()); values[1]=values[0]; values[2]=values[0]; glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,values); // cout << "pingpong=" << this->PingPongFlag << endl; // To initialize the second color buffer vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront], 0); vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D, this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+1], 0); buffer[0] = vtkgl::COLOR_ATTACHMENT0_EXT; buffer[1] = vtkgl::COLOR_ATTACHMENT1_EXT; vtkgl::DrawBuffers(2,buffer); // cout << "check before setup" << endl; this->CheckFrameBufferStatus(); this->SetupRender(ren,vol); // restore in case of composite with no cropping or streaming. buffer[0] = vtkgl::COLOR_ATTACHMENT0_EXT; buffer[1] = GL_NONE; vtkgl::DrawBuffers(2,buffer); vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D,0,0); // cout << "check after color init" << endl; this->CheckFrameBufferStatus(); if(this->NumberOfCroppingRegions>1 && (this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MAXIMUM_INTENSITY_BLEND)) { // cout << "this->MaxValueFrameBuffer="<< this->MaxValueFrameBuffer <MaxValueFrameBuffer2="<< this->MaxValueFrameBuffer2 <MaxValueFrameBuffer,0); vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D, this->MaxValueFrameBuffer2,0); buffer[0] = vtkgl::COLOR_ATTACHMENT0_EXT; buffer[1] = vtkgl::COLOR_ATTACHMENT1_EXT; vtkgl::DrawBuffers(2,buffer); if(this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) { glClearColor(1.0, 0.0, 0.0, 0.0); } else { glClearColor(0.0, 0.0, 0.0, 0.0); // for MAXIMUM_INTENSITY_BLEND } // cout << "check before clear on max" << endl; this->CheckFrameBufferStatus(); glClear(GL_COLOR_BUFFER_BIT); } if(this->NumberOfCroppingRegions>1) { // color buffer target in the color attachement 0 vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront], 0); // color buffer input is on texture unit 4. vtkgl::ActiveTexture(vtkgl::TEXTURE4); glBindTexture(GL_TEXTURE_2D,this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+1]); if(this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) { // max buffer target in the color attachment 1 vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D, this->MaxValueFrameBuffer,0); // max buffer input is on texture unit 5. vtkgl::ActiveTexture(vtkgl::TEXTURE5); glBindTexture(GL_TEXTURE_2D,this->MaxValueFrameBuffer2); } vtkgl::ActiveTexture(vtkgl::TEXTURE0); } this->CheckFrameBufferStatus(); if(this->OpacityTables!=0 && this->OpacityTables->Vector.size()!=numberOfLevels) { delete this->OpacityTables; this->OpacityTables=0; } if(this->OpacityTables==0) { this->OpacityTables=new vtkOpacityTables(numberOfLevels); } // debug code // DO NOT REMOVE the following commented line // this->ValidateProgram(); glCullFace (GL_BACK); // otherwise, we are rendering back face to initialize the zbuffer. if(!this->GeneratingCanonicalView && this->ReportProgress) { // initialize the time to avoid a progress event at the beginning. this->LastProgressEventTime=vtkTimerLog::GetUniversalTime(); } this->PrintError("PreRender end"); } //----------------------------------------------------------------------------- // Compute how each axis of a cell is projected on the viewport in pixel. // This requires to have information about the camera and about the volume. // It set the value of IgnoreSampleDistancePerPixel to true in case of // degenerated case (axes aligned with the view). //----------------------------------------------------------------------------- double vtkMitkOpenGLGPUVolumeRayCastMapper::ComputeMinimalSampleDistancePerPixel( vtkRenderer *renderer, vtkVolume *volume) { // For each of the 3 directions of a cell, compute the step in z // (world coordinate, not eye/camera coordinate) // to go to the next pixel in x. // Same for the next pixel in y. // Keep the minimum of both zstep // Then keep the minimum for the 3 directions. // in case of either the numerator or the denominator of each ratio is 0. this->IgnoreSampleDistancePerPixel=true; double result=0.0; vtkMatrix4x4 *worldToDataset=volume->GetMatrix(); vtkCamera *camera=renderer->GetActiveCamera(); vtkMatrix4x4 *eyeToWorld=camera->GetViewTransformMatrix(); vtkMatrix4x4 *eyeToDataset=vtkMatrix4x4::New(); vtkMatrix4x4::Multiply4x4(eyeToWorld,worldToDataset,eyeToDataset); int usize; int vsize; renderer->GetTiledSize(&usize,&vsize); vtkMatrix4x4 *viewportToEye=camera->GetProjectionTransformMatrix( usize/static_cast(vsize),0.0,1.0); double volBounds[6]; this->GetInput()->GetBounds(volBounds); int dims[3]; this->GetInput()->GetDimensions(dims); double v0[4]; v0[0]=volBounds[0]; v0[1]=volBounds[2]; v0[2]=volBounds[4]; v0[3]=1.0; double w0[4]; eyeToDataset->MultiplyPoint(v0,w0); double z0; if(w0[3]!=0.0) { z0=w0[2]/w0[3]; } else { z0=0.0; vtkGenericWarningMacro( "eyeToWorld transformation has some projective component." ); } double p0[4]; viewportToEye->MultiplyPoint(w0,p0); p0[0]/=p0[3]; p0[1]/=p0[3]; p0[2]/=p0[3]; bool inFrustum=p0[0]>=-1.0 && p0[0]<=1.0 && p0[1]>=-1.0 && p0[1]<=1.0 && p0[2]>=-1.0 && p0[2]<=1.0; if(inFrustum) { int dim=0; while(dim<3) { double v1[4]; int coord=0; while(coord<3) { if(coord==dim) { v1[coord]=volBounds[2*coord+1]; } else { v1[coord]=volBounds[2*coord]; // same as v0[coord]; } ++coord; } v1[3]=1.0; double w1[4]; eyeToDataset->MultiplyPoint(v1,w1); double z1; if(w1[3]!=0.0) { z1=w1[2]/w1[3]; } else { z1=0.0; vtkGenericWarningMacro( "eyeToWorld transformation has some projective component." ); } double p1[4]; viewportToEye->MultiplyPoint(w1,p1); p1[0]/=p1[3]; p1[1]/=p1[3]; p1[2]/=p1[3]; inFrustum=p1[0]>=-1.0 && p1[0]<=1.0 && p1[1]>=-1.0 && p1[1]<=1.0 && p1[2]>=-1.0 && p1[2]<=1.0; if(inFrustum) { double dx=fabs(p1[0]-p0[0]); double dy=fabs(p1[1]-p0[1]); double dz=fabs(z1-z0); dz=dz/(dims[dim]-1); dx=dx/(dims[dim]-1)*usize; dy=dy/(dims[dim]-1)*vsize; if(dz!=0.0) { if(dx!=0.0) { double d=dz/dx; if(!this->IgnoreSampleDistancePerPixel) { if(result>d) { result=d; } } else { this->IgnoreSampleDistancePerPixel=false; result=d; } } if(dy!=0.0) { double d=dz/dy; if(!this->IgnoreSampleDistancePerPixel) { if(result>d) { result=d; } } else { this->IgnoreSampleDistancePerPixel=false; result=d; } } } } ++dim; } } eyeToDataset->Delete(); if(this->IgnoreSampleDistancePerPixel) { // cout<<"ignore SampleDistancePerPixel"<ValidateProgram(); this->PrintError("before render"); if(!this->Cropping) { this->RenderWholeVolume(ren,vol); } else { this->ClipCroppingRegionPlanes(); this->RenderRegions(ren,vol); } this->PrintError("after render"); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::PostRender( vtkRenderer *ren, int numberOfScalarComponents) { this->PrintError("PostRender1"); if(this->NumberOfCroppingRegions>1) { if(this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) { vtkgl::ActiveTexture( vtkgl::TEXTURE5 ); glBindTexture(GL_TEXTURE_2D,0); } if(this->LastRayCastMethod!=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP && this->LastRayCastMethod!=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMinIP) { vtkgl::ActiveTexture( vtkgl::TEXTURE4 ); glBindTexture(GL_TEXTURE_2D,0); } } // noisetexture vtkgl::ActiveTexture(vtkgl::TEXTURE6); glBindTexture(GL_TEXTURE_2D,0); // depthtexture vtkgl::ActiveTexture(vtkgl::TEXTURE3); glBindTexture(GL_TEXTURE_2D,0); // opacity vtkgl::ActiveTexture(vtkgl::TEXTURE2); glBindTexture(GL_TEXTURE_1D,0); if(numberOfScalarComponents==1) { vtkgl::ActiveTexture(vtkgl::TEXTURE1); glBindTexture(GL_TEXTURE_1D,0); } // mask, if any if(this->MaskInput!=0) { vtkgl::ActiveTexture(vtkgl::TEXTURE7); glBindTexture(vtkgl::TEXTURE_3D_EXT,0); } // back to active texture 0. vtkgl::ActiveTexture(vtkgl::TEXTURE0); glBindTexture(vtkgl::TEXTURE_3D_EXT,0); vtkgl::UseProgram(0); this->PrintError("after UseProgram(0)"); this->CleanupRender(); this->PrintError("after CleanupRender"); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(this->SavedFrameBuffer)); this->SavedFrameBuffer=0; // Undo the viewport change we made to reduce resolution int size[2]; int lowerLeft[2]; ren->GetTiledSizeAndOrigin(size,size+1,lowerLeft,lowerLeft+1); glViewport(lowerLeft[0],lowerLeft[1], size[0], size[1]); glEnable( GL_SCISSOR_TEST ); glScissor(lowerLeft[0],lowerLeft[1], size[0], size[1]); // Render the texture to the screen - this copies the offscreen buffer // onto the screen as a texture mapped polygon this->RenderTextureToScreen(ren); this->PrintError("after RenderTextureToScreen"); glEnable(GL_DEPTH_TEST); glPopAttrib(); // restore the blending mode and function glFinish(); this->PrintError("PostRender2"); } //----------------------------------------------------------------------------- // The main render method called from the superclass //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::GPURender(vtkRenderer *ren, vtkVolume *vol) { // We've already checked that we have input vtkImageData *input = this->GetTransformedInput(); // Get the bounds of this data double bounds[6]; this->GetBounds(bounds); // Get the scalar range. First we have to get the scalars. double range[2]; vtkDataArray *scalars=this->GetScalars(input,this->ScalarMode, this->ArrayAccessMode, this->ArrayId,this->ArrayName, this->CellFlag); // How many components are there? int numberOfScalarComponents=scalars->GetNumberOfComponents(); // If it is just one, then get the range from the scalars if(numberOfScalarComponents==1) { // Warning: here, we ignore the blank cells. scalars->GetRange(range); } // If it is 3, then use the 4th component's range since that is // the component that will be passed through the scalar opacity // transfer function to look up opacity else { // Note that we've already checked data type and we know this is // unsigned char scalars->GetRange(range,3); } // The rendering work has been broken into 3 stages to support AMR // volume rendering in blocks. Here we are simply rendering the // whole volume as one block. Note that if the volume is too big // to fix into texture memory, it will be streamed through in the // RenderBlock method. this->PreRender(ren,vol,bounds,range,numberOfScalarComponents,1); if(!this->OpacityTables) this->PreRender(ren,vol,bounds,range,numberOfScalarComponents,1); if(this->LoadExtensionsSucceeded) { this->RenderBlock(ren,vol,0); this->PostRender(ren,numberOfScalarComponents); } // Let's just make sure no OpenGL errors occurred during this render this->PrintError("End GPU Render"); // If this isn't a canonical view render, then update the progress to // 1 because we are done. if (!this->GeneratingCanonicalView ) { double progress=1.0; this->InvokeEvent(vtkCommand::VolumeMapperRenderProgressEvent,&progress); ren->GetRenderWindow()->MakeCurrent(); } } //----------------------------------------------------------------------------- // Render a the whole volume. // \pre this->ProgramShader!=0 and is linked. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::RenderWholeVolume(vtkRenderer *ren, vtkVolume *vol) { double volBounds[6]; this->GetTransformedInput()->GetBounds(volBounds); this->RenderSubVolume(ren,volBounds,vol); } //----------------------------------------------------------------------------- // Sort regions from front to back. //----------------------------------------------------------------------------- class vtkRegionDistance2 { public: size_t Id; // 0<=Id<27 // square distance between camera center to region center: >=0 double Distance2; }; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- extern "C" int vtkRegionComparisonFunction(const void *x, const void *y) { double dx=static_cast(x)->Distance2; double dy=static_cast(y)->Distance2; int result; if(dxdy) { result=1; } else { result=0; } } return result; } //----------------------------------------------------------------------------- // Render a subvolume. // \pre this->ProgramShader!=0 and is linked. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::RenderRegions(vtkRenderer *ren, vtkVolume *vol) { double bounds[27][6]; double distance2[27]; double camPos[4]; ren->GetActiveCamera()->GetPosition(camPos); double volBounds[6]; this->GetInput()->GetBounds(volBounds); // Pass camera through inverse volume matrix // so that we are in the same coordinate system vol->GetMatrix( this->InvVolumeMatrix ); camPos[3] = 1.0; this->InvVolumeMatrix->Invert(); this->InvVolumeMatrix->MultiplyPoint( camPos, camPos ); if ( camPos[3] ) { camPos[0] /= camPos[3]; camPos[1] /= camPos[3]; camPos[2] /= camPos[3]; } // These are the region limits for x (first four), y (next four) and // z (last four). The first region limit is the lower bound for // that axis, the next two are the region planes along that axis, and // the final one in the upper bound for that axis. double limit[12]; size_t i; for ( i = 0; i < 3; i++ ) { limit[i*4 ] = volBounds[i*2]; limit[i*4+1] = this->ClippedCroppingRegionPlanes[i*2]; limit[i*4+2] = this->ClippedCroppingRegionPlanes[i*2+1]; limit[i*4+3] = volBounds[i*2+1]; } // For each of the 27 possible regions, find out if it is enabled, // and if so, compute the bounds and the distance from the camera // to the center of the region. size_t numRegions = 0; size_t region; for ( region = 0; region < 27; region++ ) { int regionFlag = 1<CroppingRegionFlags & regionFlag ) { // what is the coordinate in the 3x3x3 grid size_t loc[3]; loc[0] = region%3; loc[1] = (region/3)%3; loc[2] = (region/9)%3; // make sure the cropping region is not empty NEW // otherwise, we skip the region. if((limit[loc[0]]!=limit[loc[0]+1]) && (limit[loc[1]+4]!=limit[loc[1]+5]) && (limit[loc[2]+8]!=limit[loc[2]+9])) { // compute the bounds and center double center[3]; for ( i = 0; i < 3; i++ ) { bounds[numRegions][i*2 ] = limit[4*i+loc[i]]; bounds[numRegions][i*2+1] = limit[4*i+loc[i]+1]; center[i]=(bounds[numRegions][i*2]+bounds[numRegions][i*2+1])*0.5; } // compute the distance squared to the center distance2[numRegions] = (camPos[0]-center[0])*(camPos[0]-center[0]) + (camPos[1]-center[1])*(camPos[1]-center[1]) + (camPos[2]-center[2])*(camPos[2]-center[2]); // we've added one region numRegions++; } } } vtkRegionDistance2 regions[27]; i=0; while(iNumberOfCroppingRegions>=0); } //----------------------------------------------------------------------------- // slabsDataSet are position of the slabs in dataset coordinates. // slabsPoints are position of the slabs in points coordinates. // For instance, slabsDataSet[0] is the position of the plane bounding the slab // on the left of x axis of the dataset. slabsPoints[0]=0.3 means that // this plane lies between point 0 and point 1 along the x-axis. // There is no clamping/clipping according to the dataset bounds so, // slabsPoints can be negative or excess the number of points along the // corresponding axis. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::SlabsFromDatasetToIndex( double slabsDataSet[6], double slabsPoints[6]) { double *spacing=this->GetInput()->GetSpacing(); double origin[3]; // take spacing sign into account double *bds = this->GetInput()->GetBounds(); origin[0] = bds[0]; origin[1] = bds[2]; origin[2] = bds[4]; int i=0; while(i<6) { slabsPoints[i]=(slabsDataSet[i] - origin[i/2]) / spacing[i/2]; ++i; } } //----------------------------------------------------------------------------- // slabsDataSet are position of the slabs in dataset coordinates. // slabsPoints are position of the slabs in points coordinates. // For instance, slabsDataSet[0] is the position of the plane bounding the slab // on the left of x axis of the dataset. slabsPoints[0]=0.3 means that // this plane lies between point 0 and point 1 along the x-axis. // There is no clamping/clipping according to the dataset bounds so, // slabsPoints can be negative or excess the number of points along the // corresponding axis. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::SlabsFromIndexToDataset( double slabsPoints[6], double slabsDataSet[6]) { double *spacing=this->GetInput()->GetSpacing(); double origin[3]; // take spacing sign into account double *bds = this->GetInput()->GetBounds(); origin[0] = bds[0]; origin[1] = bds[2]; origin[2] = bds[4]; int i=0; while(i<6) { slabsDataSet[i]=slabsPoints[i]*spacing[i/2]+origin[i/2]; ++i; } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- class vtkStreamBlock { public: double Bounds[6]; double Extent[6]; }; //----------------------------------------------------------------------------- // Render a subvolume. bounds are in world coordinates. // \pre this->ProgramShader!=0 and is linked. //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::RenderSubVolume(vtkRenderer *ren, double bounds[6], vtkVolume *volume) { // Time to load scalar field size_t i; int wholeTextureExtent[6]; this->GetTransformedInput()->GetExtent(wholeTextureExtent); if(this->CellFlag) { i=1; while(i<6) { wholeTextureExtent[i]--; i+=2; } } // 1. Found out the extent of the subvolume double realExtent[6]; int subvolumeTextureExtent[6]; this->SlabsFromDatasetToIndex(bounds,realExtent); if(this->CellFlag) // 3D texture are celldata { // texture extents are expressed in cells in this case i=0; while(i<6) { subvolumeTextureExtent[i]=vtkMath::Floor(realExtent[i]-0.5); ++i; subvolumeTextureExtent[i]=vtkMath::Floor(realExtent[i]-0.5)+1; ++i; } } else { // texture extents are expressed in points in this case i=0; while(i<6) { subvolumeTextureExtent[i]=vtkMath::Floor(realExtent[i]); ++i; subvolumeTextureExtent[i]=vtkMath::Floor(realExtent[i])+1; // used to not have +1 ++i; } } i=0; while(i<6) { assert("check: wholeTextureExtent" && wholeTextureExtent[i]==0); if(subvolumeTextureExtent[i]wholeTextureExtent[i]) { subvolumeTextureExtent[i]=wholeTextureExtent[i]; } ++i; } assert("check: subvolume_inside_wholevolume" && subvolumeTextureExtent[0]>=wholeTextureExtent[0] && subvolumeTextureExtent[1]<=wholeTextureExtent[1] && subvolumeTextureExtent[2]>=wholeTextureExtent[2] && subvolumeTextureExtent[3]<=wholeTextureExtent[3] && subvolumeTextureExtent[4]>=wholeTextureExtent[4] && subvolumeTextureExtent[5]<=wholeTextureExtent[5]); // 2. Is this subvolume already on the GPU? // ie are the extent of the subvolume inside the loaded extent? // Find the texture (and mask). vtkstd::map::iterator it= this->ScalarsTextures->Map.find(this->GetTransformedInput()); vtkKWScalarField *texture; if(it==this->ScalarsTextures->Map.end()) { texture=0; } else { texture=(*it).second; } vtkKWMask *mask=0; if(this->MaskInput!=0) { vtkstd::map::iterator it2= this->MaskTextures->Map.find(this->MaskInput); if(it2==this->MaskTextures->Map.end()) { mask=0; } else { mask=(*it2).second; } } int loaded = texture!=0 && texture->IsLoaded() && this->GetTransformedInput()->GetMTime()<=texture->GetBuildTime() && (this->GetMaskInput() ? this->GetMaskInput()->GetMTime() <= texture->GetBuildTime() : true) && texture->GetLoadedCellFlag()==this->CellFlag; vtkIdType *loadedExtent; if(loaded) { loadedExtent=texture->GetLoadedExtent(); i=0; while(loaded && i<6) { loaded=loaded && loadedExtent[i]<=subvolumeTextureExtent[i]; ++i; loaded=loaded && loadedExtent[i]>=subvolumeTextureExtent[i]; ++i; } } if(loaded) { this->CurrentScalar=texture; vtkgl::ActiveTexture(vtkgl::TEXTURE0); this->CurrentScalar->Bind(); vtkgl::ActiveTexture(vtkgl::TEXTURE7); this->CurrentMask=mask; if(this->CurrentMask!=0) { this->CurrentMask->Bind(); } } if(!loaded) { // 3. Not loaded: try to load the whole dataset if(!this->LoadScalarField(this->GetTransformedInput(),this->MaskInput,wholeTextureExtent,volume)) { // 4. loading the whole dataset failed: try to load the subvolume if(!this->LoadScalarField(this->GetTransformedInput(),this->MaskInput, subvolumeTextureExtent, volume)) { // 5. loading the subvolume failed: stream the subvolume // 5.1 do zslabs first, if too large then cut with x or y with the // largest dimension. order of zlabs depends on sign of spacing[2] int streamTextureExtent[6]; i=0; while(i<6) { streamTextureExtent[i]=subvolumeTextureExtent[i]; ++i; } unsigned int internalFormat; unsigned int format; unsigned int type; int componentSize; this->GetTextureFormat(this->GetInput(),&internalFormat,&format,&type, &componentSize); // Enough memory? int originalTextureSize[3]; int textureSize[3]; i=0; while(i<3) { textureSize[i]=subvolumeTextureExtent[2*i+1]-subvolumeTextureExtent[2*i]+1; originalTextureSize[i]=textureSize[i]; ++i; } // Make sure loading did not fail because of theorical limits GLint width; glGetIntegerv(vtkgl::MAX_3D_TEXTURE_SIZE,&width); int clippedXY=0; int clippedZ=0; if(textureSize[0]>width) { textureSize[0]=width; clippedXY=1; } if(textureSize[1]>width) { textureSize[1]=width; clippedXY=1; } if(textureSize[2]>width) { textureSize[2]=width; clippedZ=1; } int minSize; if(this->CellFlag) { minSize=1; } else { minSize=2; } if(clippedXY) { // We cannot expect to first divide as z-slabs because it is already // clipped in another dimension. From now, just divide in the largest // dimension. bool foundSize=false; while(!foundSize && textureSize[0]>=minSize && textureSize[1]>=minSize) { foundSize=this->TestLoadingScalar(internalFormat,format,type, textureSize,componentSize); if(!foundSize) { int maxDim=0; if(textureSize[1]>textureSize[0]) { maxDim=1; } if(textureSize[2]>textureSize[maxDim]) { maxDim=2; } textureSize[maxDim]>>=1; // /=2 } } } else { // we are in cropping mode, it will be slow anyway. the case we want // to optimize is stream the all scalar field. With that in mine, // it is better to first try to send z-slabs. If even a minimal // z-slab is too big, we have to divide by x or y dimensions. In // this case, it will be slow and we can choose to keep blocks as // square as possible by dividing by the largest dimension at each // iteration. if(!clippedZ) { // we start by subdividing only if we did not already clipped // the z dimension according to the theorical limits. textureSize[2]>>=1; // /=2 } bool foundSize=false; while(!foundSize && textureSize[2]>=minSize) { foundSize=this->TestLoadingScalar(internalFormat,format,type, textureSize,componentSize); if(!foundSize) { textureSize[2]>>=1; // /=2 } } if(!foundSize) { textureSize[2]=minSize; if(textureSize[0]>textureSize[1]) { textureSize[0]>>=1; // /=2 } else { textureSize[1]>>=1; // /=2 } while(!foundSize && textureSize[0]>=minSize && textureSize[1]>=minSize) { foundSize=this->TestLoadingScalar(internalFormat,format,type, textureSize,componentSize); if(!foundSize) { if(textureSize[0]>textureSize[1]) { textureSize[0]>>=1; // /=2 } else { textureSize[1]>>=1; // /=2 } } } } if(!foundSize) { vtkErrorMacro( <<"No memory left on the GPU even for a minimal block."); return 1; // abort } } // except for the last bound. // front to back ordering // Pass camera through inverse volume matrix // so that we are in the same coordinate system double camPos[4]; vtkCamera *cam = ren->GetActiveCamera(); cam->GetPosition(camPos); volume->GetMatrix( this->InvVolumeMatrix ); camPos[3] = 1.0; this->InvVolumeMatrix->Invert(); this->InvVolumeMatrix->MultiplyPoint( camPos, camPos ); if ( camPos[3] ) { camPos[0] /= camPos[3]; camPos[1] /= camPos[3]; camPos[2] /= camPos[3]; } // 5.2 iterate of each stream of the subvolume and render it: // point scalar: on the first block, the first point is not shared // blockExtent is always expressed in point, not in texture // extent. size_t remainder[3]; i=0; while(i<3) { remainder[i]=static_cast( (originalTextureSize[i]-textureSize[i])%(textureSize[i]-1)); if(remainder[i]>0) { remainder[i]=1; } ++i; } size_t counts[3]; counts[0]=static_cast((originalTextureSize[0]-textureSize[0]) /(textureSize[0]-1)); counts[0]+=remainder[0]+1; counts[1]=static_cast((originalTextureSize[1]-textureSize[1]) /(textureSize[1]-1)); counts[1]+=remainder[1]+1; counts[2]=static_cast((originalTextureSize[2]-textureSize[2]) /(textureSize[2]-1)); counts[2]+=remainder[2]+1; size_t count=counts[0]*counts[1]*counts[2]; double blockExtent[6]; vtkStreamBlock *blocks=new vtkStreamBlock[count]; vtkRegionDistance2 *sortedBlocks=new vtkRegionDistance2[count]; // iterate over z,y,x size_t blockId=0; size_t zIndex=0; blockExtent[4]=realExtent[4]; blockExtent[5]=vtkMath::Floor(blockExtent[4])+textureSize[2]; if(!this->CellFlag) { blockExtent[5]--; } if(blockExtent[5]>realExtent[5]) { blockExtent[5]=realExtent[5]; } while(zIndexCellFlag) { blockExtent[3]--; } if(blockExtent[3]>realExtent[3]) { blockExtent[3]=realExtent[3]; } size_t yIndex=0; while(yIndexCellFlag) { blockExtent[1]--; } if(blockExtent[1]>realExtent[1]) { blockExtent[1]=realExtent[1]; } size_t xIndex=0; while(xIndexSlabsFromIndexToDataset(blockExtent,blockBounds); // compute the bounds and center double center[3]; i=0; while(i<3) { center[i]=(blockBounds[i*2]+blockBounds[i*2+1])*0.5; ++i; } // compute the distance squared to the center double distance2=(camPos[0]-center[0])*(camPos[0]-center[0])+ (camPos[1]-center[1])*(camPos[1]-center[1]) + (camPos[2]-center[2])*(camPos[2]-center[2]); i=0; while(i<6) { blocks[blockId].Bounds[i]=blockBounds[i]; blocks[blockId].Extent[i]=blockExtent[i]; ++i; } sortedBlocks[blockId].Id=blockId; sortedBlocks[blockId].Distance2=distance2; ++blockId; blockExtent[0]=blockExtent[1]; blockExtent[1]=blockExtent[0]+textureSize[0]; if(!this->CellFlag) { blockExtent[1]--; } if(blockExtent[1]>realExtent[1]) { blockExtent[1]=realExtent[1]; } ++xIndex; } // while x blockExtent[2]=blockExtent[3]; blockExtent[3]=blockExtent[2]+textureSize[1]; if(!this->CellFlag) { blockExtent[3]--; } if(blockExtent[3]>realExtent[3]) { blockExtent[3]=realExtent[3]; } ++yIndex; } // while y blockExtent[4]=blockExtent[5]; blockExtent[5]=blockExtent[4]+textureSize[2]; if(!this->CellFlag) { blockExtent[5]--; } if(blockExtent[5]>realExtent[5]) { blockExtent[5]=realExtent[5]; } ++zIndex; } // while z assert("check: valid_number_of_blocks" && blockId==count); qsort(sortedBlocks,static_cast(count), sizeof(vtkRegionDistance2), vtkRegionComparisonFunction); // loop over all blocks we need to render i=0; int abort=0; while(!abort && i < count) // 1) //count) { size_t k=sortedBlocks[i].Id; int blockTextureExtent[6]; int j; if(this->CellFlag) // 3D texture are celldata { // texture extents are expressed in cells in this case j=0; while(j<6) { blockTextureExtent[j]=vtkMath::Floor(blocks[k].Extent[j]); ++j; } } else { // texture extents are expressed in points in this case j=0; while(j<6) { blockTextureExtent[j]=vtkMath::Floor(blocks[k].Extent[j]); ++j; blockTextureExtent[j]=vtkMath::Floor(blocks[k].Extent[j]); if(blockTextureExtent[j]LoadScalarField(this->GetInput(),this->MaskInput, blockTextureExtent, volume)) { cout<<"Loading the streamed block FAILED!!!!!"<CurrentScalar->GetLoadedExtent(); float lowBounds[3]; float highBounds[3]; if(!this->CurrentScalar->GetLoadedCellFlag()) // points { j=0; while(j<3) { double delta= static_cast(loadedExtent[j*2+1]-loadedExtent[j*2]); lowBounds[j]=static_cast((blocks[k].Extent[j*2]-static_cast(loadedExtent[j*2]))/delta); highBounds[j]=static_cast((blocks[k].Extent[j*2+1]-static_cast(loadedExtent[j*2]))/delta); ++j; } } else // cells { j=0; while(j<3) { double delta= static_cast(loadedExtent[j*2+1]-loadedExtent[j*2]); lowBounds[j]=static_cast((blocks[k].Extent[j*2]-0.5-static_cast(loadedExtent[j*2]))/delta); highBounds[j]=static_cast((blocks[k].Extent[j*2+1]-0.5-static_cast(loadedExtent[j*2]))/delta); ++j; } } // bounds have to be normalized. There are used in the shader // as bounds to a value used to sample a texture. assert("check: positive_low_bounds0" && lowBounds[0]>=0.0); assert("check: positive_low_bounds1" && lowBounds[1]>=0.0); assert("check: positive_low_bounds2" && lowBounds[2]>=0.0); assert("check: increasing_bounds0" && lowBounds[0]<=highBounds[0]); assert("check: increasing_bounds1" && lowBounds[1]<=highBounds[1]); assert("check: increasing_bounds2" && lowBounds[2]<=highBounds[2]); assert("check: high_bounds0_less_than1" && highBounds[0]<=1.0); assert("check: high_bounds1_less_than1" && highBounds[1]<=1.0); assert("check: high_bounds2_less_than1" && highBounds[2]<=1.0); GLint lb; lb=vtkgl::GetUniformLocation(static_cast(this->ProgramShader), "lowBounds"); this->PrintError("get uniform low bounds"); if(lb!=-1) { vtkgl::Uniform3f(lb, lowBounds[0],lowBounds[1],lowBounds[2]); this->PrintError("set uniform low bounds"); } else { vtkErrorMacro(<<" lowBounds is not a uniform variable."); } GLint hb; hb=vtkgl::GetUniformLocation(static_cast(this->ProgramShader), "highBounds"); this->PrintError("get uniform high bounds"); if(hb!=-1) { vtkgl::Uniform3f(hb, highBounds[0],highBounds[1],highBounds[2]); this->PrintError("set uniform high bounds"); } else { vtkErrorMacro(<<" highBounds is not a uniform variable."); } this->PrintError("uniform low/high bounds block"); // other sub-volume rendering code this->LoadProjectionParameters(ren,volume); this->ClipBoundingBox(ren,blocks[k].Bounds,volume); abort=this->RenderClippedBoundingBox(1,i,count,ren->GetRenderWindow()); if (!abort) { this->CopyFBOToTexture(); } this->PrintError("render clipped block 1"); ++i; } delete[] blocks; delete[] sortedBlocks; return abort; } } } loadedExtent=this->CurrentScalar->GetLoadedExtent(); // low bounds and high bounds are in texture coordinates. float lowBounds[3]; float highBounds[3]; if(!this->CurrentScalar->GetLoadedCellFlag()) // points { i=0; while(i<3) { double delta= static_cast(loadedExtent[i*2+1]-loadedExtent[i*2]+1); lowBounds[i]=static_cast((realExtent[i*2]+0.5-static_cast(loadedExtent[i*2]))/delta); highBounds[i]=static_cast((realExtent[i*2+1]+0.5-static_cast(loadedExtent[i*2]))/delta); ++i; } } else // cells { i=0; while(i<3) { double delta= static_cast(loadedExtent[i*2+1]-loadedExtent[i*2]+1); // this->LoadedExtent[i*2]==0, texcoord starts at 0, if realExtent==0 // otherwise, texcoord start at 1/2N // this->LoadedExtent[i*2]==wholeTextureExtent[i*2+1], texcoord stops at 1, if realExtent==wholeTextureExtent[i*2+1]+1 // otherwise it stop at 1-1/2N // N is the number of texels in the loadedtexture not the number of // texels in the whole texture. lowBounds[i]=static_cast((realExtent[i*2]-static_cast(loadedExtent[i*2]))/delta); highBounds[i]=static_cast((realExtent[i*2+1]-static_cast(loadedExtent[i*2]))/delta); ++i; } } assert("check: positive_low_bounds0" && lowBounds[0]>=0.0); assert("check: positive_low_bounds1" && lowBounds[1]>=0.0); assert("check: positive_low_bounds2" && lowBounds[2]>=0.0); assert("check: increasing_bounds0" && lowBounds[0]<=highBounds[0]); assert("check: increasing_bounds1" && lowBounds[1]<=highBounds[1]); assert("check: increasing_bounds2" && lowBounds[2]<=highBounds[2]); assert("check: high_bounds0_less_than1" && highBounds[0]<=1.0); assert("check: high_bounds1_less_than1" && highBounds[1]<=1.0); assert("check: high_bounds2_less_than1" && highBounds[2]<=1.0); GLint lb; lb=vtkgl::GetUniformLocation(static_cast(this->ProgramShader), "lowBounds"); this->PrintError("get uniform low bounds"); if(lb!=-1) { vtkgl::Uniform3f(lb, lowBounds[0],lowBounds[1],lowBounds[2]); this->PrintError("set uniform low bounds"); } else { vtkErrorMacro(<<" lowBounds is not a uniform variable."); } GLint hb; hb=vtkgl::GetUniformLocation(static_cast(this->ProgramShader), "highBounds"); this->PrintError("get uniform high bounds"); if(hb!=-1) { vtkgl::Uniform3f(hb, highBounds[0],highBounds[1],highBounds[2]); this->PrintError("set uniform high bounds"); } else { vtkErrorMacro(<<" highBounds is not a uniform variable."); } this->PrintError("uniform low/high bounds"); // other sub-volume rendering code this->LoadProjectionParameters(ren,volume); this->ClipBoundingBox(ren,bounds,volume); int abort=this->RenderClippedBoundingBox(1,0,1,ren->GetRenderWindow()); if (!abort) { this->CopyFBOToTexture(); } this->PrintError("render clipped 1"); return abort; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::LoadProjectionParameters( vtkRenderer *ren, vtkVolume *vol) { vtkMatrix4x4 *worldToDataset=vol->GetMatrix(); vtkMatrix4x4 *datasetToWorld=this->TempMatrix[0]; vtkMatrix4x4::Invert(worldToDataset,datasetToWorld); double *bounds=this->CurrentScalar->GetLoadedBounds(); double dx=bounds[1]-bounds[0]; double dy=bounds[3]-bounds[2]; double dz=bounds[5]-bounds[4]; // worldToTexture matrix is needed // Compute change-of-coordinate matrix from world space to texture space. vtkMatrix4x4 *worldToTexture=this->TempMatrix[2]; vtkMatrix4x4 *datasetToTexture=this->TempMatrix[1]; // Set the matrix datasetToTexture->Zero(); datasetToTexture->SetElement(0,0,dx); datasetToTexture->SetElement(1,1,dy); datasetToTexture->SetElement(2,2,dz); datasetToTexture->SetElement(3,3,1.0); datasetToTexture->SetElement(0,3,bounds[0]); datasetToTexture->SetElement(1,3,bounds[2]); datasetToTexture->SetElement(2,3,bounds[4]); // worldToTexture=worldToDataSet*dataSetToTexture vtkMatrix4x4::Multiply4x4(worldToDataset,datasetToTexture,worldToTexture); // NEW int parallelProjection=ren->GetActiveCamera()->GetParallelProjection(); // cout << "actualSampleDistance=" << this->ActualSampleDistance << endl; if(parallelProjection) { // Unit vector of the direction of projection in world space. double dirWorld[4]; double dir[4]; ren->GetActiveCamera()->GetDirectionOfProjection(dirWorld); dirWorld[3]=0.0; // direction in dataset space. datasetToWorld->MultiplyPoint(dirWorld,dir); // incremental vector: // direction in texture space times sample distance in world space. dir[0]=dir[0]*this->ActualSampleDistance/dx; dir[1]=dir[1]*this->ActualSampleDistance/dy; dir[2]=dir[2]*this->ActualSampleDistance/dz; GLint rayDir; rayDir=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"parallelRayDirection"); if(rayDir!=-1) { vtkgl::Uniform3f(rayDir,static_cast(dir[0]), static_cast(dir[1]), static_cast(dir[2])); } else { vtkErrorMacro(<<"parallelRayDirection is not a uniform variable."); } //cout<<"rayDir="<GetActiveCamera()->GetPosition(cameraPosWorld); cameraPosWorld[3]=1.0; // we use homogeneous coordinates. datasetToWorld->MultiplyPoint(cameraPosWorld,cameraPosDataset); // From homogeneous to cartesian coordinates. if(cameraPosDataset[3]!=1.0) { double ratio=1/cameraPosDataset[3]; cameraPosDataset[0]*=ratio; cameraPosDataset[1]*=ratio; cameraPosDataset[2]*=ratio; } cameraPosTexture[0] = (cameraPosDataset[0]-bounds[0])/dx; cameraPosTexture[1] = (cameraPosDataset[1]-bounds[2])/dy; cameraPosTexture[2] = (cameraPosDataset[2]-bounds[4])/dz; // Only make sense for the vectorial part of the homogeneous matrix. // coefMatrix=transposeWorldToTexture*worldToTexture // we re-cycle the datasetToWorld pointer with a different name vtkMatrix4x4 *transposeWorldToTexture=this->TempMatrix[1]; // transposeWorldToTexture={^t}worldToTexture vtkMatrix4x4::Transpose(worldToTexture,transposeWorldToTexture); vtkMatrix4x4 *coefMatrix=this->TempMatrix[1]; vtkMatrix4x4::Multiply4x4(transposeWorldToTexture,worldToTexture, coefMatrix); GLint uCameraPosition; uCameraPosition=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"cameraPosition"); if(uCameraPosition!=-1) { vtkgl::Uniform3f(uCameraPosition, static_cast(cameraPosTexture[0]), static_cast(cameraPosTexture[1]), static_cast(cameraPosTexture[2])); } else { vtkErrorMacro(<<"cameraPosition is not a uniform variable."); } GLint uSampleDistance; uSampleDistance=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"sampleDistance"); if(uSampleDistance!=-1) { vtkgl::Uniform1f(uSampleDistance,this->ActualSampleDistance); } else { vtkErrorMacro(<<"sampleDistance is not a uniform variable."); } GLint uMatrix1; uMatrix1=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"matrix1"); if(uMatrix1!=-1) { vtkgl::Uniform3f(uMatrix1, static_cast(coefMatrix->GetElement(0,0)), static_cast(coefMatrix->GetElement(1,1)), static_cast(coefMatrix->GetElement(2,2))); } else { vtkErrorMacro(<<"matrix1 is not a uniform variable."); } GLint uMatrix2; uMatrix2=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"matrix2"); if(uMatrix2!=-1) { vtkgl::Uniform3f(uMatrix2, static_cast(2*coefMatrix->GetElement(0,1)), static_cast(2*coefMatrix->GetElement(1,2)), static_cast(2*coefMatrix->GetElement(0,2))); } else { vtkErrorMacro(<<"matrix2 is not a uniform variable."); } } this->PrintError("after uniforms for projection"); // Change-of-coordinate matrix from Eye space to texture space. vtkMatrix4x4 *eyeToTexture=this->TempMatrix[1]; vtkMatrix4x4 *eyeToWorld=ren->GetActiveCamera()->GetViewTransformMatrix(); vtkMatrix4x4::Multiply4x4(eyeToWorld,worldToTexture,eyeToTexture); GLfloat matrix[16];// used sometimes as 3x3, sometimes as 4x4. double *raw=eyeToTexture->Element[0]; int index; int column; int row; int shadeMethod=this->LastShade; if(shadeMethod==vtkMitkOpenGLGPUVolumeRayCastMapperShadeYes) { index=0; column=0; while(column<3) { row=0; while(row<3) { // cout << "index=" << index << " row*4+column=" << row*4+column << endl; matrix[index]=static_cast(raw[row*4+column]); ++index; ++row; } ++column; } GLint uEyeToTexture3; uEyeToTexture3=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"eyeToTexture3"); this->PrintError("after getUniform eyeToTexture3"); if(uEyeToTexture3!=-1) { vtkgl::UniformMatrix3fv(uEyeToTexture3,1,GL_FALSE,matrix); } else { vtkErrorMacro(<<"eyeToTexture3 is not a uniform variable."); } this->PrintError("after Uniform eyeToTexture3"); index=0; column=0; while(column<4) { row=0; while(row<4) { // cout << "index=" << index << " row*4+column=" << row*4+column << endl; matrix[index]=static_cast(raw[row*4+column]); ++index; ++row; } ++column; } GLint uEyeToTexture4; uEyeToTexture4=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"eyeToTexture4"); if(uEyeToTexture4!=-1) { vtkgl::UniformMatrix4fv(uEyeToTexture4,1,GL_FALSE,matrix); } else { vtkErrorMacro(<<"eyeToTexture4 is not a uniform variable."); } } eyeToTexture->Invert(); index=0; column=0; while(column<4) { row=0; while(row<4) { // cout << "index=" << index << " row*4+column=" << row*4+column << endl; matrix[index]=static_cast(raw[row*4+column]); ++index; ++row; } ++column; } this->PrintError("before GetUniformLocation TextureToEye"); GLint uTextureToEye; uTextureToEye=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"textureToEye"); this->PrintError("after GetUniformLocation TextureToEye"); if(uTextureToEye!=-1) { vtkgl::UniformMatrix4fv(uTextureToEye,1,GL_FALSE,matrix); } else { vtkErrorMacro(<<"textureToEye is not a uniform variable."); } this->PrintError("after UniformMatrxix TextureToEye"); if(shadeMethod==vtkMitkOpenGLGPUVolumeRayCastMapperShadeYes) { eyeToTexture->Transpose(); index=0; column=0; while(column<3) { row=0; while(row<3) { // cout << "index=" << index << " row*4+column=" << row*4+column << endl; matrix[index]=static_cast(raw[row*4+column]); ++index; ++row; } ++column; } GLint uTranposeTextureToEye; uTranposeTextureToEye=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"transposeTextureToEye"); if(uTranposeTextureToEye!=-1) { vtkgl::UniformMatrix3fv(uTranposeTextureToEye,1,GL_FALSE,matrix); } else { vtkErrorMacro(<<"transposeTextureToEye is not a uniform variable."); } float cellScale[3]; // 1/(2*Step) float cellStep[3]; // Step vtkIdType *loadedExtent=this->CurrentScalar->GetLoadedExtent(); cellScale[0]=static_cast(static_cast( loadedExtent[1]-loadedExtent[0])*0.5); cellScale[1]=static_cast(static_cast( loadedExtent[3]-loadedExtent[2])*0.5); cellScale[2]=static_cast(static_cast( loadedExtent[5]-loadedExtent[4])*0.5); cellStep[0]=static_cast(1.0/static_cast( loadedExtent[1]-loadedExtent[0])); cellStep[1]=static_cast(1.0/static_cast( loadedExtent[3]-loadedExtent[2])); cellStep[2]=static_cast(1.0/static_cast( loadedExtent[5]-loadedExtent[4])); GLint uCellScale; uCellScale=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"cellScale"); if(uCellScale!=-1) { vtkgl::Uniform3f(uCellScale,cellScale[0],cellScale[1],cellScale[2]); } else { vtkErrorMacro(<<"error: cellScale is not a uniform variable."); } GLint uCellStep; uCellStep=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"cellStep"); if(uCellStep!=-1) { vtkgl::Uniform3f(uCellStep,cellStep[0],cellStep[1],cellStep[2]); } else { vtkErrorMacro(<<"error: cellStep is not a uniform variable."); } } } //----------------------------------------------------------------------------- // Concatenate the header string, projection type code and method to the // final fragment code in this->FragmentCode. // \pre valid_raycastMethod: raycastMethod>= // vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP && // raycastMethod<=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMinIPFourDependent //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::BuildProgram(int parallelProjection, int raycastMethod, int shadeMethod, int componentMethod) { assert("pre: valid_raycastMethod" && raycastMethod>= vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP && raycastMethod<=vtkMitkOpenGLGPUVolumeRayCastMapperMethodCompositeMask); GLuint fs; // cout<<"projection="<ProgramShader, vtkgl::INFO_LOG_LENGTH,¶ms); if(params>0) { char *buffer=new char[params]; vtkgl::GetProgramInfoLog(this->ProgramShader,params,0,buffer); cout<<"validation log: "<GetEnabledString(glIsEnabled(GL_LIGHTING))<GetEnabledString(glIsEnabled(GL_LIGHTING))<(value); cout<<"active texture is "<<(activeTexture-vtkgl::TEXTURE0)<(value); cout<<"light\t| status\t| ambient\t| diffuse\t| specular\t| position\t| spot direction\t| spot exponent\t| spot cutoff\t| k0\t| k1\t| k2"<=0 // \post valid_result: result>=x //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::PowerOfTwoGreaterOrEqual(int x) { assert("pre: positive_x" && x>=0); int result=1; while(result=x); return result; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::UpdateNoiseTexture() { if(this->NoiseTextureId==0) { GLuint noiseTextureObject; glGenTextures(1,&noiseTextureObject); this->NoiseTextureId=static_cast(noiseTextureObject); vtkgl::ActiveTexture(vtkgl::TEXTURE6); glBindTexture(GL_TEXTURE_2D,noiseTextureObject); GLsizei size=128; // 1024; // Power of two value GLint maxSize; const float factor=0.1f; // const float factor=1.0f; const float amplitude=0.5f*factor; // something positive. // amplitude=0.5. noise between -0.5 +0.5. add some +0.5 shift. glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxSize); if(size>maxSize) { size=maxSize; } if(this->NoiseTexture!=0 && this->NoiseTextureSize!=size) { delete[] this->NoiseTexture; this->NoiseTexture=0; } if(this->NoiseTexture==0) { this->NoiseTexture=new float[size*size]; this->NoiseTextureSize=size; vtkPerlinNoise *noiseGenerator=vtkPerlinNoise::New(); noiseGenerator->SetFrequency(size,1.0,1.0); noiseGenerator->SetPhase(0.0,0.0,0.0); noiseGenerator->SetAmplitude(amplitude); int j=0; while(jNoiseTexture[j*size+i]=0.0; //amplitude+static_cast(noiseGenerator->EvaluateFunction(i,j,0.0)); ++i; } ++j; } noiseGenerator->Delete(); } glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,size,size,0,GL_RED,GL_FLOAT, this->NoiseTexture); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); GLfloat borderColor[4]={0.0,0.0,0.0,0.0}; glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR,borderColor); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); vtkgl::ActiveTexture(vtkgl::TEXTURE0); } } // ---------------------------------------------------------------------------- // Description: // Return how much the dataset has to be reduced in each dimension to // fit on the GPU. If the value is 1.0, there is no need to reduce the // dataset. // \pre the calling thread has a current OpenGL context. // \pre mapper_supported: IsRenderSupported(renderer->GetRenderWindow(),0) // The computation is based on hardware limits (3D texture indexable size) // and MaxMemoryInBytes. // \post valid_i_ratio: ratio[0]>0 && ratio[0]<=1.0 // \post valid_j_ratio: ratio[1]>0 && ratio[1]<=1.0 // \post valid_k_ratio: ratio[2]>0 && ratio[2]<=1.0 void vtkMitkOpenGLGPUVolumeRayCastMapper::GetReductionRatio(double ratio[3]) { // Compute texture size int i; int wholeTextureExtent[6]; this->GetInput()->GetExtent(wholeTextureExtent); if(this->CellFlag) // if we deal with cell data { i=1; while(i<6) { wholeTextureExtent[i]--; i+=2; } } // Indexable hardware limits GLint maxSize; glGetIntegerv(vtkgl::MAX_3D_TEXTURE_SIZE,&maxSize); vtkIdType rTextureSize[3]; double dMaxSize=static_cast(maxSize); i=0; while(i<3) { double textureSize=wholeTextureExtent[2*i+1]-wholeTextureExtent[2*i]+1; if(textureSize>maxSize) { ratio[i]=dMaxSize/textureSize; } else { ratio[i]=1.0; // no reduction } rTextureSize[i]=static_cast(floor(textureSize*ratio[i])); ++i; } // Data memory limits. vtkDataArray *scalars=this->GetScalars(this->GetInput(),this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, this->CellFlag); int scalarType=scalars->GetDataType(); vtkIdType size=rTextureSize[0]*rTextureSize[1]*rTextureSize[2] *vtkAbstractArray::GetDataTypeSize(scalarType) *scalars->GetNumberOfComponents(); if(size>static_cast(this->MaxMemoryInBytes) *static_cast(this->MaxMemoryFraction)) { double r=static_cast(this->MaxMemoryInBytes) *static_cast(this->MaxMemoryFraction)/static_cast(size); double r3=pow(r,1.0/3.0); // try the keep reduction ratio uniform to avoid artifacts. bool reduced[3]; i=0; int count=0; while(i<3) { vtkIdType newSize=static_cast( floor(static_cast(rTextureSize[i])*r3)); reduced[i]=newSize>=1; if(reduced[i]) { ++count; } ++i; } if(count<3) // some axis cannot be reduced { double r2=sqrt(r); count=0; i=0; while(i<3) { if(reduced[i]) { vtkIdType newSize=static_cast( floor(static_cast(rTextureSize[i])*r2)); reduced[i]=newSize>=1; if(reduced[i]) { ++count; } } ++i; } if(count<2) // we can only reduce one axis { i=0; while(i<3) { if(reduced[i]) { ratio[i]*=r; } ++i; } } else // we can reduce two axes { i=0; while(i<3) { if(reduced[i]) { ratio[i]*=r2; } ++i; } } } else // we can reduce all three axes { i=0; while(i<3) { ratio[i]*=r3; ++i; } } } assert("post: valid_i_ratio" && ratio[0]>0 && ratio[0]<=1.0); assert("post: valid_j_ratio" && ratio[1]>0 && ratio[1]<=1.0); assert("post: valid_k_ratio" && ratio[2]>0 && ratio[2]<=1.0); } //----------------------------------------------------------------------------- // Standard print method //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); } -#endif \ No newline at end of file +#endif