vtk9/Rendering/Volume/vtkUnstructuredGridBunykRay...

280 lines
9.3 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkUnstructuredGridBunykRayCastFunction.h
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.
=========================================================================*/
/**
* @class vtkUnstructuredGridBunykRayCastFunction
* @brief a superclass for ray casting functions
*
*
* vtkUnstructuredGridBunykRayCastFunction is a concrete implementation of a
* ray cast function for unstructured grid data. This class was based on the
* paper "Simple, Fast, Robust Ray Casting of Irregular Grids" by Paul Bunyk,
* Arie Kaufmna, and Claudio Silva. This method is quite memory intensive
* (with extra explicit copies of the data) and therefore should not be used
* for very large data. This method assumes that the input data is composed
* entirely of tetras - use vtkDataSetTriangleFilter before setting the input
* on the mapper.
*
* The basic idea of this method is as follows:
*
* 1) Enumerate the triangles. At each triangle have space for some
* information that will be used during rendering. This includes
* which tetra the triangles belong to, the plane equation and the
* Barycentric coefficients.
*
* 2) Keep a reference to all four triangles for each tetra.
*
* 3) At the beginning of each render, do the precomputation. This
* includes creating an array of transformed points (in view
* coordinates) and computing the view dependent info per triangle
* (plane equations and barycentric coords in view space)
*
* 4) Find all front facing boundary triangles (a triangle is on the
* boundary if it belongs to only one tetra). For each triangle,
* find all pixels in the image that intersect the triangle, and
* add this to the sorted (by depth) intersection list at each
* pixel.
*
* 5) For each ray cast, traverse the intersection list. At each
* intersection, accumulate opacity and color contribution
* per tetra along the ray until you reach an exiting triangle
* (on the boundary).
*
*
* @sa
* vtkUnstructuredGridVolumeRayCastMapper
*/
#ifndef vtkUnstructuredGridBunykRayCastFunction_h
#define vtkUnstructuredGridBunykRayCastFunction_h
#include "vtkRenderingVolumeModule.h" // For export macro
#include "vtkUnstructuredGridVolumeRayCastFunction.h"
class vtkRenderer;
class vtkVolume;
class vtkUnstructuredGridVolumeRayCastMapper;
class vtkMatrix4x4;
class vtkPiecewiseFunction;
class vtkColorTransferFunction;
class vtkUnstructuredGridBase;
class vtkIdList;
class vtkDoubleArray;
class vtkDataArray;
// We manage the memory for the list of intersections ourself - this is the
// storage used. We keep 10,000 elements in each array, and we can have up to
// 1,000 arrays.
#define VTK_BUNYKRCF_MAX_ARRAYS 10000
#define VTK_BUNYKRCF_ARRAY_SIZE 10000
class VTKRENDERINGVOLUME_EXPORT vtkUnstructuredGridBunykRayCastFunction
: public vtkUnstructuredGridVolumeRayCastFunction
{
public:
static vtkUnstructuredGridBunykRayCastFunction* New();
vtkTypeMacro(vtkUnstructuredGridBunykRayCastFunction, vtkUnstructuredGridVolumeRayCastFunction);
void PrintSelf(ostream& os, vtkIndent indent) override;
/**
* Called by the ray cast mapper at the start of rendering
*/
void Initialize(vtkRenderer* ren, vtkVolume* vol) override;
/**
* Called by the ray cast mapper at the end of rendering
*/
void Finalize() override;
VTK_NEWINSTANCE
vtkUnstructuredGridVolumeRayCastIterator* NewIterator() override;
// Used to store each triangle - made public because of the
// templated function
class Triangle
{
public:
vtkIdType PointIndex[3];
vtkIdType ReferredByTetra[2];
double P1X, P1Y;
double P2X, P2Y;
double Denominator;
double A, B, C, D;
Triangle* Next;
};
// Used to store each intersection for the pixel rays - made
// public because of the templated function
class Intersection
{
public:
Triangle* TriPtr;
double Z;
Intersection* Next;
};
/**
* Is the point x, y, in the given triangle? Public for
* access from the templated function.
*/
int InTriangle(double x, double y, Triangle* triPtr);
/**
* Access to an internal structure for the templated method.
*/
double* GetPoints() { return this->Points; }
///@{
/**
* Access to an internal structure for the templated method.
*/
vtkGetObjectMacro(ViewToWorldMatrix, vtkMatrix4x4);
///@}
///@{
/**
* Access to an internal structure for the templated method.
*/
vtkGetVectorMacro(ImageOrigin, int, 2);
///@}
///@{
/**
* Access to an internal structure for the templated method.
*/
vtkGetVectorMacro(ImageViewportSize, int, 2);
///@}
/**
* Access to an internal structure for the templated method.
*/
Triangle** GetTetraTriangles() { return this->TetraTriangles; }
/**
* Access to an internal structure for the templated method.
*/
Intersection* GetIntersectionList(int x, int y)
{
return this->Image[y * this->ImageSize[0] + x];
}
protected:
vtkUnstructuredGridBunykRayCastFunction();
~vtkUnstructuredGridBunykRayCastFunction() override;
// These are cached during the initialize method so that they do not
// need to be passed into subsequent CastRay calls.
vtkRenderer* Renderer;
vtkVolume* Volume;
vtkUnstructuredGridVolumeRayCastMapper* Mapper;
// Computed during the initialize method - if something is
// wrong (no mapper, no volume, no input, etc.) then no rendering
// will actually be performed.
int Valid;
// These are the transformed points
int NumberOfPoints;
double* Points;
// This is the matrix that will take a transformed point back
// to world coordinates
vtkMatrix4x4* ViewToWorldMatrix;
// This is the intersection list per pixel in the image
Intersection** Image;
// This is the size of the image we are computing (which does
// not need to match the screen size)
int ImageSize[2];
// Since we may only be computing a subregion of the "full" image,
// this is the origin of the region we are computing. We must
// subtract this origin from any pixel (x,y) locations before
// accessing the pixel in this->Image (which represents only the
// subregion)
int ImageOrigin[2];
// This is the full size of the image
int ImageViewportSize[2];
// These are values saved for the building of the TriangleList. Basically
// we need to check if the data has changed in some way.
vtkUnstructuredGridBase* SavedTriangleListInput;
vtkTimeStamp SavedTriangleListMTime;
// This is a memory intensive algorithm! For each tetra in the
// input data we create up to 4 triangles (we don't create duplicates)
// This is the TriangleList. Then, for each tetra we keep track of
// the pointer to each of its four triangles - this is the
// TetraTriangles. We also keep a duplicate list of points
// (transformed into view space) - these are the Points.
Triangle** TetraTriangles;
vtkIdType TetraTrianglesSize;
Triangle* TriangleList;
// Compute whether a boundary triangle is front facing by
// looking at the fourth point in the tetra to see if it is
// in front (triangle is backfacing) or behind (triangle is
// front facing) the plane containing the triangle.
int IsTriangleFrontFacing(Triangle* triPtr, vtkIdType tetraIndex);
// The image contains lists of intersections per pixel - we
// need to clear this during the initialization phase for each
// render.
void ClearImage();
// This is the memory buffer used to build the intersection
// lists. We do our own memory management here because allocating
// a bunch of small elements during rendering is too slow.
Intersection* IntersectionBuffer[VTK_BUNYKRCF_MAX_ARRAYS];
int IntersectionBufferCount[VTK_BUNYKRCF_MAX_ARRAYS];
// This method replaces new for creating a new element - it
// returns one from the big block already allocated (it
// allocates another big block if necessary)
void* NewIntersection();
// This method is used during the initialization process to
// check the validity of the objects - missing information
// such as the volume, renderer, mapper, etc. will be flagged
// and reported.
int CheckValidity(vtkRenderer* ren, vtkVolume* vol);
// This method is used during the initialization process to
// transform the points to view coordinates
void TransformPoints();
// This method is used during the initialization process to
// create the list of triangles if the data has changed
void UpdateTriangleList();
// This method is used during the initialization process to
// update the view dependent information in the triangle list
void ComputeViewDependentInfo();
// This method is used during the initialization process to
// compute the intersections for each pixel with the boundary
// triangles.
void ComputePixelIntersections();
private:
vtkUnstructuredGridBunykRayCastFunction(const vtkUnstructuredGridBunykRayCastFunction&) = delete;
void operator=(const vtkUnstructuredGridBunykRayCastFunction&) = delete;
};
#endif