/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkUnstructuredGridVolumeZSweepMapper.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 "vtkUnstructuredGridVolumeZSweepMapper.h" #include "vtkObjectFactory.h" #include "vtkUnstructuredGrid.h" #include "vtkTimerLog.h" #include "vtkVolume.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkRayCastImageDisplayHelper.h" #include "vtkTransform.h" #include "vtkCamera.h" #include "vtkGenericCell.h" #include "vtkPriorityQueue.h" #include "vtkIdList.h" #include "vtkVolumeProperty.h" #include "vtkColorTransferFunction.h" #include "vtkPiecewiseFunction.h" #include "vtkUnstructuredGridPreIntegration.h" #include "vtkUnstructuredGridPartialPreIntegration.h" #include "vtkUnstructuredGridHomogeneousRayIntegrator.h" #include "vtkDoubleArray.h" #include "vtkDataArray.h" #include "vtkPolyData.h" #include "vtkCellArray.h" #include "vtkXMLPolyDataWriter.h" #include "vtkPointData.h" #include #include // memset() #include #include // do not remove the following line: //#define BACK_TO_FRONT enum { VTK_VALUES_X_INDEX=0, // world coordinate VTK_VALUES_Y_INDEX, // world coordinate VTK_VALUES_Z_INDEX, // world coordinate VTK_VALUES_SCALAR_INDEX, VTK_VALUES_SIZE // size of a value array }; // Internal classes //----------------------------------------------------------------------------- // Store the result of the scan conversion at some pixel. class vtkPixelListEntry { public: vtkPixelListEntry() { } void Init(double values[VTK_VALUES_SIZE], double zView) { this->Zview=zView; int i=0; while(iValues[i]=values[i]; ++i; } } // Return the interpolated values at this pixel. double *GetValues() { return this->Values; } // Return the interpolated z coordinate in view space at this pixel. double GetZview() const { return this->Zview; } vtkPixelListEntry *GetPrevious() { return this->Previous; } vtkPixelListEntry *GetNext() { return this->Next; } void SetPrevious(vtkPixelListEntry *e) { this->Previous=e; } void SetNext(vtkPixelListEntry *e) { this->Next=e; } protected: double Values[VTK_VALUES_SIZE]; double Zview; // List structure: both for the free block list (one-way) and any // pixel list (two-way) vtkPixelListEntry *Next; // List structure: only for the pixel list (two-way) vtkPixelListEntry *Previous; private: vtkPixelListEntry(const vtkPixelListEntry &other); vtkPixelListEntry &operator=(const vtkPixelListEntry &other); }; //----------------------------------------------------------------------------- // Cache the projection of a vertex class vtkVertexEntry { public: vtkVertexEntry() {} vtkVertexEntry(int screenX, int screenY, double xWorld, double yWorld, double zWorld, double zView, double scalar, double invW) :ScreenX(screenX),ScreenY(screenY),Zview(zView),InvW(invW) { this->Values[VTK_VALUES_X_INDEX]=xWorld; this->Values[VTK_VALUES_Y_INDEX]=yWorld; this->Values[VTK_VALUES_Z_INDEX]=zWorld; this->Values[VTK_VALUES_SCALAR_INDEX]=scalar; } void Set(int screenX, int screenY, double xWorld, double yWorld, double zWorld, double zView, double scalar, double invW) { this->ScreenX=screenX; this->ScreenY=screenY; this->Zview=zView; this->Values[VTK_VALUES_X_INDEX]=xWorld; this->Values[VTK_VALUES_Y_INDEX]=yWorld; this->Values[VTK_VALUES_Z_INDEX]=zWorld; this->Values[VTK_VALUES_SCALAR_INDEX]=scalar; this->InvW=invW; } int GetScreenX() { return this->ScreenX; } int GetScreenY() { return this->ScreenY; } double *GetValues() { return this->Values; } double GetZview() { return this->Zview; } double GetInvW() { return this->InvW; } vtkVertexEntry(const vtkVertexEntry &vtkNotUsed(other)) {} protected: int ScreenX; int ScreenY; double Values[VTK_VALUES_SIZE]; double Zview; double InvW; private: vtkVertexEntry &operator=(const vtkVertexEntry &other); }; //----------------------------------------------------------------------------- // Abstract interface for an edge of a triangle in the screen space. // Used during scan-conversion. class vtkScreenEdge { public: // If the edge is a composite edge (top+bottom) switch to the bottom edge. // Otherwise, do nothing. virtual void OnBottom(int skipped, int y) { if(!skipped) { this->NextLine(y); } } // Increment edge state to the next line. virtual void NextLine(int y)=0; // Increment edge state to the next deltaY line. virtual void SkipLines(int deltaY, int y)=0; // Return the abscissa for the current line. virtual int GetX()=0; // Return the projected values for the current line. They are linearly // incrementally interpolated in view space. The actual values are // given by projectedValue/InvW. This is the way the values in world space // are incrementally (not linearly) interpolated in view space. virtual double *GetPValues()=0; // Return 1/W, linearly interpolated in view space. virtual double GetInvW()=0; // Return Z in view coordinates, linearly interpolated in view space. virtual double GetZview()=0; protected: // Destructor. virtual ~vtkScreenEdge() {} }; //----------------------------------------------------------------------------- // Do an incremental traversing of an edge based on an Y increment. enum { VTK_CASE_VERTICAL=0, VTK_CASE_MOSTLY_VERTICAL, VTK_CASE_DIAGONAL, VTK_CASE_HORIZONTAL_BEGIN, VTK_CASE_HORIZONTAL_END, VTK_CASE_HORIZONTAL_MS, // most significant pixel VTK_CASE_VERTICAL_IN_TO_OUT, // with edge equation VTK_CASE_VERTICAL_OUT_TO_IN, VTK_CASE_HORIZONTAL_IN_TO_OUT, VTK_CASE_HORIZONTAL_OUT_TO_IN }; // We use an edge equation as described in: // Juan Pineda // A Parallel Algorithm for Polygon Rasterization // In Computer Graphics, Volume 22, Number 4, August 1988 // SIGGRAPH'88, Atlanta, August 1-5, 1988. // pages 17--20. // or in: // Marc Olano and Trey Greer // Triangle Scan Conversion using 2D Homogeneous Coordinates // In 1997 SIGGRAPH/Eurographics Workshop // pages 89--95. // http://www.cs.unc.edu/~olano/papers/2dh-tri/2dh-tri.pdf #define MOST_SIGNIFICANT #define EDGE_EQUATION #define HORI_EDGE_EQUATION //#define STRICTLY_INSIDE class vtkSimpleScreenEdge : public vtkScreenEdge { public: // Initialize the edge by the vertices v0 and v2 (ordered in y) // `onRight' is true if the edge in on the right side of the triangle. void Init(vtkVertexEntry *v0, vtkVertexEntry *v2, int dx20, int dy20, int onRight) { double z0=v0->GetZview(); double z2=v2->GetZview(); double invW0=v0->GetInvW(); double invW2=v2->GetInvW(); double pv0[VTK_VALUES_SIZE]; double pv2[VTK_VALUES_SIZE]; int i=0; while(iGetValues()[i]*invW0; this->PValues[i]=pv0[i]; pv2[i]=v2->GetValues()[i]*invW2; ++i; } this->InvW=invW0; this->Zview=z0; int x0=v0->GetScreenX(); int x2=v2->GetScreenX(); this->X0=x0; this->X2=x2; this->V2=v2; this->X=x0; if(dx20==0) { this->Case=VTK_CASE_VERTICAL; double invDy20=1.0/dy20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDy20; ++i; } this->DinvW=(invW2-invW0)*invDy20; this->Dz=(z2-z0)*invDy20; } else { if(dx20>0) { this->IncX=1; if(dx20>dy20) { // Mostly horizontal #ifdef HORI_EDGE_EQUATION if(onRight) { this->Case=VTK_CASE_HORIZONTAL_IN_TO_OUT; } else { this->Case=VTK_CASE_HORIZONTAL_OUT_TO_IN; } this->Error=0; this->SDy=dy20; this->XStep=dx20/dy20; // integral division this->Dx=dx20-this->XStep*this->SDy; double invDy20=1.0/dy20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDy20; ++i; } this->DinvW=(invW2-invW0)*invDy20; this->Dz=(z2-z0)*invDy20; #else #ifdef MOST_SIGNIFICANT this->Case=VTK_CASE_HORIZONTAL_MS; this->XStep=dx20/dy20; // integral division this->Dy=dy20; this->Dy2=dy20<<1; // *2 this->Error=0; this->ErrorStep=(dx20-this->XStep*dy20)<<1; // 2*r, dx=q*dy+r, rDpv[i]=(pv2[i]-pv0[i])*invDx20; this->PValuesStep[i]=this->Dpv[i]*this->XStep; ++i; } this->DinvW=(invW2-invW0)*invDx20; this->Dz=(z2-z0)*invDx20; this->InvWStep=this->DinvW*this->XStep; this->ZStep=this->Dz*this->XStep; #else if(!onRight) { this->Case=VTK_CASE_HORIZONTAL_BEGIN; this->First=1; this->Dy2=dy20<<1; // *2 this->Dx2=dx20<<1; // *2 this->Error=dx20; this->XStep=dx20/dy20; // integral division this->ErrorStep=this->XStep*this->Dy2; double invDx20=1.0/dx20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDx20; this->PValuesStep[i]=this->Dpv[i]*this->XStep; ++i; } this->DinvW=(invW2-invW0)*invDx20; this->Dz=(z2-z0)*invDx20; this->InvWStep=this->DinvW*this->XStep; this->ZStep=this->Dz*this->XStep; } else { this->Case=VTK_CASE_HORIZONTAL_END; this->InvW2=invW2; i=0; while(iPValues2[i]=pv2[i]; ++i; } this->Zview2=z2; this->Dy2=dy20<<1; // *2 this->Dx2=dx20<<1; // *2 this->Error=dx20; this->XStep=dx20/dy20; this->ErrorStep=this->XStep*this->Dy2; double invDx20=1.0/dx20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDx20; this->PValuesStep[i]=this->Dpv[i]*this->XStep; ++i; } this->DinvW=(invW2-invW0)*invDx20; this->Dz=(z2-z0)*invDx20; this->InvWStep=this->DinvW*this->XStep; this->ZStep=this->Dz*this->XStep; while(this->ErrorDx2) { this->X+=this->IncX; this->InvW+= this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; this->Error+=this->Dy2; } this->Error-=this->Dx2; this->X-=this->IncX; this->InvW-= this->DinvW; i=0; while(iPValues[i]-=this->Dpv[i]; ++i; } this->Zview-=this->Dz; } #endif #endif // EDGE_EQUATION } else { if(dx20==dy20) { this->Case=VTK_CASE_DIAGONAL; double invDy20=1.0/dy20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDy20; ++i; } this->DinvW=(invW2-invW0)*invDy20; this->Dz=(z2-z0)*invDy20; } else { #ifdef EDGE_EQUATION if(onRight) { this->Case=VTK_CASE_VERTICAL_IN_TO_OUT; } else { this->Case=VTK_CASE_VERTICAL_OUT_TO_IN; } this->Error=0; this->SDy=dy20; this->Dx=dx20; double invDy20=1.0/dy20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDy20; ++i; } this->DinvW=(invW2-invW0)*invDy20; this->Dz=(z2-z0)*invDy20; #else this->Case=VTK_CASE_MOSTLY_VERTICAL; this->Dx2=dx20<<1; // *2 this->Dy2=dy20<<1; // *2 this->Error=dy20; double invDy20=1.0/dy20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDy20; ++i; } this->DinvW=(invW2-invW0)*invDy20; this->Dz=(z2-z0)*invDy20; #endif } } } else { this->IncX=-1; if(-dx20>dy20) { // Mostly horizontal #ifdef HORI_EDGE_EQUATION if(onRight) { this->Case=VTK_CASE_HORIZONTAL_OUT_TO_IN; } else { this->Case=VTK_CASE_HORIZONTAL_IN_TO_OUT; } this->Error=0; this->SDy=-dy20; this->XStep=dx20/dy20; // integral division this->Dx=dx20+this->XStep*this->SDy; double invDy20=1.0/dy20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDy20; ++i; } this->DinvW=(invW2-invW0)*invDy20; this->Dz=(z2-z0)*invDy20; #else #ifdef MOST_SIGNIFICANT this->Case=VTK_CASE_HORIZONTAL_MS; this->XStep=dx20/dy20; // integral division this->Dy=dy20; this->Dy2=dy20<<1; // *2 this->Error=0; this->ErrorStep=(dx20+this->XStep*dy20)<<1; // 2*r, dx=q*dy+r, rDpv[i]=(pv2[i]-pv0[i])*invDx20; this->PValuesStep[i]=-this->Dpv[i]*this->XStep; ++i; } this->DinvW=(invW2-invW0)*invDx20; this->Dz=(z2-z0)*invDx20; this->InvWStep=-this->DinvW*this->XStep; this->ZStep=-this->Dz*this->XStep; #else if(onRight) { this->Case=VTK_CASE_HORIZONTAL_BEGIN; this->First=1; this->Dy2=dy20<<1; // *2 this->Dx2=(-dx20)<<1; // *2 this->Error=-dx20; this->XStep=dx20/dy20; this->ErrorStep=-this->XStep*this->Dy2; double invDx20=-1.0/dx20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDx20; this->PValuesStep[i]=-this->Dpv[i]*this->XStep; ++i; } this->DinvW=(invW2-invW0)*invDx20; this->Dz=(z2-z0)*invDx20; this->InvWStep=-this->DinvW*this->XStep; this->ZStep=-this->Dz*this->XStep; } else { this->Case=VTK_CASE_HORIZONTAL_END; this->InvW2=invW2; i=0; while(iPValues2[i]=pv2[i]; ++i; } this->Zview2=z2; this->Dy2=dy20<<1; // *2 this->Dx2=(-dx20)<<1; // *2 this->Error=-dx20; this->XStep=dx20/dy20; this->ErrorStep=-this->XStep*this->Dy2; double invDx20=-1.0/dx20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDx20; this->PValuesStep[i]=-this->Dpv[i]*this->XStep; ++i; } this->DinvW=(invW2-invW0)*invDx20; this->Dz=(z2-z0)*invDx20; this->InvWStep=-this->DinvW*this->XStep; this->ZStep=-this->Dz*this->XStep; while(this->ErrorDx2) { this->X+=this->IncX; this->InvW+= this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; this->Error+=this->Dy2; } this->Error-=this->Dx2; this->X-=this->IncX; this->InvW-= this->DinvW; i=0; while(iPValues[i]-=this->Dpv[i]; ++i; } this->Zview-=this->Dz; } #endif #endif // EDGE_EQUATION } else { if(dx20==-dy20) { this->Case=VTK_CASE_DIAGONAL; double invDy20=1.0/dy20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDy20; ++i; } this->DinvW=(invW2-invW0)*invDy20; this->Dz=(z2-z0)*invDy20; } else { #ifdef EDGE_EQUATION if(onRight) { this->Case=VTK_CASE_VERTICAL_OUT_TO_IN; } else { this->Case=VTK_CASE_VERTICAL_IN_TO_OUT; } this->Error=0; this->SDy=-dy20; this->Dx=dx20; double invDy20=1.0/dy20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDy20; ++i; } this->DinvW=(invW2-invW0)*invDy20; this->Dz=(z2-z0)*invDy20; #else this->Case=VTK_CASE_MOSTLY_VERTICAL; this->Dx2=(-dx20)<<1; // *2 this->Dy2=dy20<<1; // *2 this->Error=dy20; double invDy20=1.0/dy20; i=0; while(iDpv[i]=(pv2[i]-pv0[i])*invDy20; ++i; } this->DinvW=(invW2-invW0)*invDy20; this->Dz=(z2-z0)*invDy20; #endif } } } } } // Check that the current abscissa is in the range given by the vertices. int ValidXRange() { if(this->X0<=this->X2) { return (this->X>=this->X0) && (this->X<=this->X2); } else { return (this->X>=this->X2) && (this->X<=this->X0); } } int GetX() { // assert("pre: valid_range" && ValidXRange() ); return this->X; } double GetInvW() { return this->InvW; } double *GetPValues() { return this->PValues; } double GetZview() { return this->Zview; } void NextLine(int y) { int i; switch(this->Case) { case VTK_CASE_VERTICAL: // nothing to do with X this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; break; case VTK_CASE_DIAGONAL: // X this->X+=this->IncX; this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; break; case VTK_CASE_MOSTLY_VERTICAL: // X this->Error+=this->Dx2; if(this->Error>=this->Dy2) { this->Error-=this->Dy2; this->X+=this->IncX; } this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; break; case VTK_CASE_VERTICAL_OUT_TO_IN: this->Error-=this->Dx; if(this->SDy>0) { #ifdef STRICTLY_INSIDE if(this->Error<=0) #else if(this->Error<0) // we are no more on the right side #endif { this->Error+=this->SDy; #ifdef STRICTLY_INSIDE assert("check: positive_equation" && this->Error>0); #else assert("check: positive_equation" && this->Error>=0); #endif this->X+=this->IncX; } } else { #ifdef STRICTLY_INSIDE if(this->Error>=0) // we are no more on the left side #else if(this->Error>0) // we are no more on the left side #endif { this->Error+=this->SDy; #ifdef STRICTLY_INSIDE // assert("check: negative_equation" && this->Error>0); #else assert("check: negative_equation" && this->Error<=0); #endif this->X+=this->IncX; } } // Interpolate the values on inc y this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; break; case VTK_CASE_VERTICAL_IN_TO_OUT: this->Error+=this->SDy-this->Dx; if(this->SDy<0) { #ifdef STRICTLY_INSIDE if(this->Error<=0) // out: too far on left #else if(this->Error<0) // out: too far on left #endif { this->Error-=this->SDy; #ifdef STRICTLY_INSIDE assert("check: positive_equation" && this->Error>0); #else assert("check: positive_equation" && this->Error>=0); #endif } else { this->X+=this->IncX; } } else { #ifdef STRICTLY_INSIDE if(this->Error>=0) // out: too far on right #else if(this->Error>0) // out: too far on right #endif { this->Error-=this->SDy; #ifdef STRICTLY_INSIDE assert("check: negative_equation" && this->Error<0); #else assert("check: negative_equation" && this->Error<=0); #endif } else { this->X+=this->IncX; } } // Interpolate the values on inc y this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; break; case VTK_CASE_HORIZONTAL_OUT_TO_IN: this->Error-=this->Dx; this->X+=this->XStep; if(this->SDy>0) { #ifdef STRICTLY_INSIDE if(this->Error<=0) // we are no more on the right side #else if(this->Error<0) // we are no more on the right side #endif { this->Error+=this->SDy; #ifdef STRICTLY_INSIDE assert("check: positive_equation" && this->Error>0); #else assert("check: positive_equation" && this->Error>=0); #endif this->X+=this->IncX; } } else { #ifdef STRICTLY_INSIDE if(this->Error>=0) // we are no more on the left side #else if(this->Error>0) // we are no more on the left side #endif { this->Error+=this->SDy; #ifdef STRICTLY_INSIDE assert("check: negative_equation" && this->Error<0); #else assert("check: negative_equation" && this->Error<=0); #endif this->X+=this->IncX; } } // Interpolate the values on inc y this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; break; case VTK_CASE_HORIZONTAL_IN_TO_OUT: this->Error+=this->SDy-this->Dx; this->X+=this->XStep; if(this->SDy<0) { #ifdef STRICTLY_INSIDE if(this->Error<=0) // out: too far on left #else if(this->Error<0) // out: too far on left #endif { this->Error-=this->SDy; #ifdef STRICTLY_INSIDE // assert("check: positive_equation" && this->Error>0); #else assert("check: positive_equation" && this->Error>=0); #endif } else { this->X+=this->IncX; } } else { #ifdef STRICTLY_INSIDE if(this->Error>=0) // out: too far on right #else if(this->Error>0) // out: too far on right #endif { this->Error-=this->SDy; #ifdef STRICTLY_INSIDE // assert("check: negative_equation" && this->Error<0); #else assert("check: negative_equation" && this->Error<=0); #endif } else { this->X+=this->IncX; } } // Interpolate the values on inc y this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; break; case VTK_CASE_HORIZONTAL_BEGIN: if(this->First) { this->First=0; } else { this->X+=this->XStep; this->InvW+=this->InvWStep; i=0; while(iPValues[i]+=this->PValuesStep[i]; ++i; } this->Zview+=this->ZStep; this->Error+=this->ErrorStep; } while(this->ErrorDx2) { this->X+=this->IncX; this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; this->Error+=this->Dy2; } this->Error-=this->Dx2; break; case VTK_CASE_HORIZONTAL_END: if(y==this->V2->GetScreenY()) { this->X=this->V2->GetScreenX(); i=0; while(iPValues[i]=this->PValues2[i]; ++i; } this->Zview=this->Zview2; this->InvW=this->InvW2; } else { this->X+=this->XStep; this->InvW+=this->InvWStep; i=0; while(iPValues[i]+=this->PValuesStep[i]; ++i; } this->Zview+=this->ZStep; this->Error+=this->ErrorStep; while(this->ErrorDx2) { this->X+=this->IncX; this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; this->Error+=this->Dy2; } this->Error-=this->Dx2; } break; case VTK_CASE_HORIZONTAL_MS: this->Error+=this->ErrorStep; if(this->Error>=this->Dy) { this->Error-=this->Dy2; this->X+=this->XStep+this->IncX; this->InvW+=this->InvWStep+this->DinvW; i=0; while(iPValues[i]+=this->PValuesStep[i]+this->Dpv[i]; ++i; } this->Zview+=this->ZStep+this->Dz; } else { this->X+=this->XStep; this->InvW+=this->InvWStep; i=0; while(iPValues[i]+=this->PValuesStep[i]; ++i; } this->Zview+=this->ZStep; } break; } } void SkipLines(int deltaY, int y) { if(deltaY==1) { this->NextLine(0); return; } int firstDeltaY; int i; switch(this->Case) { case VTK_CASE_VERTICAL: // nothing to do with X this->InvW+=this->DinvW*deltaY; i=0; while(iPValues[i]+=this->Dpv[i]*deltaY; ++i; } this->Zview+=this->Dz*deltaY; break; case VTK_CASE_DIAGONAL: // X this->X+=this->IncX*deltaY; this->InvW+=this->DinvW*deltaY; i=0; while(iPValues[i]+=this->Dpv[i]*deltaY; ++i; } this->Zview+=this->Dz*deltaY; break; case VTK_CASE_MOSTLY_VERTICAL: // X this->Error+=this->Dx2*deltaY; while(this->Error>=this->Dy2) { this->Error-=this->Dy2; this->X+=this->IncX; } this->InvW+=this->DinvW*deltaY; i=0; while(iPValues[i]+=this->Dpv[i]*deltaY; ++i; } this->Zview+=this->Dz*deltaY; break; case VTK_CASE_VERTICAL_OUT_TO_IN: this->Error-=this->Dx*deltaY; if(this->SDy>0) { #ifdef STRICTLY_INSIDE while(this->Error<=0) // we are no more on the right side #else while(this->Error<0) // we are no more on the right side #endif { this->Error+=this->SDy; this->X+=this->IncX; } } else { #ifdef STRICTLY_INSIDE while(this->Error>=0) // we are no more on the left side #else while(this->Error>0) // we are no more on the left side #endif { this->Error+=this->SDy; this->X+=this->IncX; } } // Interpolate the values on inc y this->InvW+=this->DinvW*deltaY; i=0; while(iPValues[i]+=this->Dpv[i]*deltaY; ++i; } this->Zview+=this->Dz*deltaY; break; case VTK_CASE_VERTICAL_IN_TO_OUT: this->Error+=(this->SDy-this->Dx)*deltaY; this->X+=this->IncX*deltaY; if(this->SDy<0) { #ifdef STRICTLY_INSIDE while(this->Error<=0) // out: too far on left #else while(this->Error<0) // out: too far on left #endif { this->Error-=this->SDy; this->X-=this->IncX; } } else { #ifdef STRICTLY_INSIDE while(this->Error>=0) // out: too far on right #else while(this->Error>0) // out: too far on right #endif { this->Error-=this->SDy; this->X-=this->IncX; } } // Interpolate the values on inc y this->InvW+=this->DinvW*deltaY; i=0; while(iPValues[i]+=this->Dpv[i]*deltaY; ++i; } this->Zview+=this->Dz*deltaY; break; case VTK_CASE_HORIZONTAL_OUT_TO_IN: this->Error-=this->Dx*deltaY; this->X+=this->XStep*deltaY; if(this->SDy>0) { #ifdef STRICTLY_INSIDE while(this->Error<=0) // we are no more on the right side #else while(this->Error<0) // we are no more on the right side #endif { this->Error+=this->SDy; this->X+=this->IncX; } } else { #ifdef STRICTLY_INSIDE while(this->Error>=0) // we are no more on the left side #else while(this->Error>0) // we are no more on the left side #endif { this->Error+=this->SDy; this->X+=this->IncX; } } // Interpolate the values on inc y this->InvW+=this->DinvW*deltaY; i=0; while(iPValues[i]+=this->Dpv[i]*deltaY; ++i; } this->Zview+=this->Dz*deltaY; break; case VTK_CASE_HORIZONTAL_IN_TO_OUT: this->Error+=(this->SDy-this->Dx)*deltaY; this->X+=(this->XStep+this->IncX)*deltaY; // this->X+=this->IncX*deltaY; if(this->SDy<0) { #ifdef STRICTLY_INSIDE while(this->Error<=0) // out: too far on left #else while(this->Error<0) // out: too far on left #endif { this->Error-=this->SDy; this->X-=this->IncX; } } else { #ifdef STRICTLY_INSIDE while(this->Error>=0) // out: too far on right #else while(this->Error>0) // out: too far on right #endif { this->Error-=this->SDy; this->X-=this->IncX; } } // Interpolate the values on inc y this->InvW+=this->DinvW*deltaY; i=0; while(iPValues[i]+=this->Dpv[i]*deltaY; ++i; } this->Zview+=this->Dz*deltaY; break; case VTK_CASE_HORIZONTAL_BEGIN: if(this->First) { this->First=0; firstDeltaY=deltaY-1; } else { firstDeltaY=deltaY; } this->X+=this->XStep*firstDeltaY; this->InvW+=this->InvWStep*firstDeltaY; i=0; while(iPValues[i]+=this->PValuesStep[i]*firstDeltaY; ++i; } this->Zview+=this->ZStep*firstDeltaY; this->Error+=this->ErrorStep*firstDeltaY; while(this->ErrorDx2) { this->X+=this->IncX; this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; this->Error+=this->Dy2; } this->Error-=this->Dx2; break; case VTK_CASE_HORIZONTAL_END: if(y==this->V2->GetScreenY()) { this->X=this->V2->GetScreenX(); i=0; while(iPValues[i]=this->PValues2[i]; ++i; } this->Zview=this->Zview2; this->InvW=this->InvW2; } else { this->X+=this->XStep*deltaY; this->InvW+=this->InvWStep*deltaY; i=0; while(iPValues[i]+=this->PValuesStep[i]*deltaY; ++i; } this->Zview+=this->ZStep*deltaY; this->Error+=this->ErrorStep*deltaY; while(this->ErrorDx2) { this->X+=this->IncX; this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; this->Error+=this->Dy2; } this->Error-=this->Dx2; } break; case VTK_CASE_HORIZONTAL_MS: this->Error+=this->ErrorStep*deltaY; this->X+=this->XStep*deltaY; this->InvW+=this->InvWStep*deltaY; i=0; while(iPValues[i]+=this->PValuesStep[i]*deltaY; ++i; } this->Zview+=this->ZStep*deltaY; while(this->Error>=this->Dy) { this->Error-=this->Dy2; this->X+=this->IncX; this->InvW+=this->DinvW; i=0; while(iPValues[i]+=this->Dpv[i]; ++i; } this->Zview+=this->Dz; } break; } } protected: int Case; int Error; // error to the mid-point int Dx2; // 2*dx int Dy2; // 2*dy int First; // use only with VTK_CASE_HORIZONTAL_BEGIN case int XStep; // dx/dy int ErrorStep; // XStep*Dy2 vtkVertexEntry *V2; int IncX; // -1 or 1 int X; // Current abscissa int X0; // for debugging int X2; // for debugging // Slope of 1/w double DinvW; // Current 1/W double InvW; // DinvW*XStep double InvWStep; // 1/W at the end vertex double InvW2; // Slope of the z coordinate in view space double Dz; // current z in view space double Zview; // Dz*XStep double ZStep; // z coordinate in view space at the end vertex double Zview2; // Slope of each projected values on the edge double Dpv[VTK_VALUES_SIZE]; // Current projected values double PValues[VTK_VALUES_SIZE]; // Dpv*XStep double PValuesStep[VTK_VALUES_SIZE]; // Values at the end vertex. double PValues2[VTK_VALUES_SIZE]; int Dy; // VTK_HORIZONTAL_MS int SDy; // VTK_VERTICAL_LEFT/RIGHT int Dx; // VTK_VERTICAL_LEFT/RIGHT }; //----------------------------------------------------------------------------- // During rasterization of a triangle, there is always one side with two // edges and the other side with a single edge. // This class manages the side with the two edges called top and bottom edges. class vtkDoubleScreenEdge :public vtkScreenEdge { public: void Init(vtkVertexEntry *v0, vtkVertexEntry *v1, vtkVertexEntry *v2, int dx10, int dy10, int onRight) { this->Current=0; if(dy10!=0) { this->Top.Init(v0,v1,dx10,dy10,onRight); this->Current=&this->Top; } int dx21=v2->GetScreenX()-v1->GetScreenX(); int dy21=v2->GetScreenY()-v1->GetScreenY(); if(dy21!=0) { this->Bottom.Init(v1,v2,dx21,dy21,onRight); if(this->Current==0) { this->Current=&this->Bottom; } } } int GetX() { return this->Current->GetX(); } double GetInvW() { return this->Current->GetInvW(); } double GetZview() { return this->Current->GetZview(); } double *GetPValues() { return this->Current->GetPValues(); } void OnBottom(int skipped, int y) { this->Current=&this->Bottom; this->Current->OnBottom(skipped,y); } void NextLine(int y) { this->Current->NextLine(y); } void SkipLines(int deltaY, int y) { this->Current->SkipLines(deltaY,y); } protected: vtkSimpleScreenEdge Top; vtkSimpleScreenEdge Bottom; vtkScreenEdge *Current; }; //----------------------------------------------------------------------------- // Horizontal span between two points of two edges. // Used during scan-conversion. // It interpolates the values along the span. class vtkSpan { public: // Initialize the span from the left abcissa x0 and the right absissa x1 and // from 1/W, the projected values and the z coordinate in view space at // thoses points. Set the current state to the left point. void Init(int x0, double invW0, double pValues0[VTK_VALUES_SIZE], // projected values double zView0, int x1, double invW1, double pValues1[VTK_VALUES_SIZE], // projected values double zView1) { // assert("pre: dx>=0" && x1-x0>=0); // x0=x1: the span is just a point int i; if(x0!=x1) { double invDx10=1.0/(x1-x0); i=0; while(iDpv[i]=(pValues1[i]-pValues0[i])*invDx10; ++i; } this->DinvW=(invW1-invW0)*invDx10; this->Dz=(zView1-zView0)*invDx10; } else { i=0; while(iDpv[i]=0; ++i; } this->DinvW=0; this->Dz=0; } this->Zview=zView0; this->InvW=invW0; i=0; double w=1/this->InvW; while(iPValues[i]=pValues0[i]; this->Values[i]=this->PValues[i]*w; ++i; } this->X=x0; this->X1=x1; } // Is the current state after the right point? int IsAtEnd() { return this->X>this->X1; } // Current abscissa. int GetX() { return this->X; } // Current values. double *GetValues() { return this->Values; } // Current z coordinate in view space. double GetZview() { return this->Zview; } // Go the next abscissa from left to right. void NextPixel() { ++this->X; this->InvW+=this->DinvW; int i=0; double w=1/this->InvW; while(iPValues[i]+=this->Dpv[i]; this->Values[i]=this->PValues[i]*w; ++i; } this->Zview+=this->Dz; } protected: int X1; // abscissa at the right point. int X; // current abscissa // Slope of 1/w double DinvW; // current 1/W double InvW; // Slope of the z coordinate in view space double Dz; // current z coordinate in view space double Zview; // Slope of each projected values on the span double Dpv[VTK_VALUES_SIZE]; // Current projected values double PValues[VTK_VALUES_SIZE]; // Current values: Values=PValues/InvW double Values[VTK_VALUES_SIZE]; }; // Pimpl (i.e. private implementation) idiom //typedef vtkstd::list vtkPixelList; class vtkPixelListEntryBlock { public: vtkPixelListEntryBlock(vtkIdType size) { assert("pre: positive_size" && size>0); this->Size=size; this->Next=0; this->Array=new vtkPixelListEntry[size]; this->Last=this->Array+size-1; // link each entry to the next one vtkPixelListEntry *p; vtkPixelListEntry *q; p=this->Array; q=p+1; vtkIdType i=1; while(iSetNext(q); ++i; p=q; ++q; } p->SetNext(0); } ~vtkPixelListEntryBlock() { delete[] this->Array; } vtkIdType GetSize() { return this->Size; } vtkPixelListEntryBlock *GetNext() { return this->Next; } vtkPixelListEntry *GetFirst() { return this->Array; } vtkPixelListEntry *GetLast() { return this->Last; } void SetNext(vtkPixelListEntryBlock *other) { this->Next=other; } protected: vtkIdType Size; vtkPixelListEntryBlock *Next; vtkPixelListEntry *Array; vtkPixelListEntry *Last; }; const vtkIdType VTK_PIXEL_BLOCK_SIZE=64; class vtkPixelListEntryMemory { public: vtkPixelListEntryMemory() { this->FirstBlock=new vtkPixelListEntryBlock(VTK_PIXEL_BLOCK_SIZE); this->FirstFreeElement=this->FirstBlock->GetFirst(); this->Size=VTK_PIXEL_BLOCK_SIZE; } ~vtkPixelListEntryMemory() { vtkPixelListEntryBlock *p=this->FirstBlock; vtkPixelListEntryBlock *q; while(p!=0) { q=p->GetNext(); delete p; p=q; } } vtkPixelListEntry *AllocateEntry() { if(this->FirstFreeElement==0) { this->AllocateBlock(this->Size<<1); // this->AllocateBlock(BLOCK_SIZE); } vtkPixelListEntry *result=this->FirstFreeElement; this->FirstFreeElement=result->GetNext(); assert("post: result_exists" && result!=0); return result; } void FreeEntry(vtkPixelListEntry *e) { assert("pre: e_exists" && e!=0); // the following line works even if this->FirstFreeElement==0 e->SetNext(this->FirstFreeElement); this->FirstFreeElement=e; } void FreeSubList(vtkPixelListEntry *first, vtkPixelListEntry *last) { assert("pre: first_exists" && first!=0); assert("pre: last_exists" && last!=0); // pre: first==last can be true // the following line works even if this->FirstFreeElement==0 last->SetNext(this->FirstFreeElement); this->FirstFreeElement=first; } protected: void AllocateBlock(vtkIdType size) { assert("pre: positive_size" && size>0); vtkPixelListEntryBlock *b=new vtkPixelListEntryBlock(size); this->Size+=size; // Update the block linked list: starts with the new block b->SetNext(this->FirstBlock); this->FirstBlock=b; // Update the free element linked list. // It works even if this->FirstFreeElement==0 b->GetLast()->SetNext(this->FirstFreeElement); this->FirstFreeElement=b->GetFirst(); } vtkPixelListEntryBlock *FirstBlock; vtkPixelListEntry *FirstFreeElement; vtkIdType Size; // overall size, in number of elements, not in bytes }; class vtkPixelList { public: vtkPixelList() { this->Size=0; } vtkPixelListEntry *GetFirst() { assert("pre: not_empty" && this->Size>0); return this->First; } vtkIdType GetSize() { return this->Size; } void AddAndSort(vtkPixelListEntry *p) { assert("pre: p_exists" && p!=0); if(this->Size==0) { p->SetPrevious(0); p->SetNext(0); this->First=p; this->Last=p; } else { vtkPixelListEntry *it=this->Last; int sorted=0; double z=p->GetZview(); while(!sorted && it!=0) { #ifdef BACK_TO_FRONT sorted=it->GetZview()>=z; #else sorted=it->GetZview()<=z; #endif if(!sorted) { it=it->GetPrevious(); } } if(it==0) // first element { p->SetPrevious(0); p->SetNext(this->First); // this->First==0 is handled by case size==0 this->First->SetPrevious(p); this->First=p; } else { if(it->GetNext()==0) // last element { it->SetNext(p); p->SetPrevious(it); p->SetNext(0); this->Last=p; } else // general case { vtkPixelListEntry *q=it->GetNext(); q->SetPrevious(p); p->SetNext(q); p->SetPrevious(it); it->SetNext(p); } } } ++this->Size; } // the return pointer is used by the memory manager. void RemoveFirst(vtkPixelListEntryMemory *mm) { assert("pre: not_empty" && this->Size>0); assert("pre: mm_exists" && mm!=0); vtkPixelListEntry *p=this->First; if(this->Size>1) { this->First=p->GetNext(); this->First->SetPrevious(0); } --this->Size; mm->FreeEntry(p); } // the return pointer on the first element is used by the memory manager. void Clear(vtkPixelListEntryMemory *mm) { assert("pre: mm_exists" && mm!=0); if(this->Size>0) { // it works even if first==last mm->FreeSubList(this->First,this->Last); this->Size=0; } } protected: vtkIdType Size; vtkPixelListEntry *First; vtkPixelListEntry *Last; }; //----------------------------------------------------------------------------- // Store the pixel lists for all the frame. class vtkPixelListFrame { public: typedef vtkstd::vector VectorType; vtkPixelListFrame(int size) :Vector(size) { } // Return width*height vtkIdType GetSize() { return this->Vector.size(); } // Return the size of the list at pixel `i'. vtkIdType GetListSize(int i) { assert("pre: valid_i" && i>=0 && iGetSize()); return this->Vector[i].GetSize(); } // Add a value the pixel list of pixel `i' and sort it in the list. void AddAndSort(int i, vtkPixelListEntry *pixelEntry) { assert("pre: valid_i" && i>=0 && iGetSize()); assert("pre: pixelEntry_exists" && pixelEntry!=0); this->Vector[i].AddAndSort(pixelEntry); } // Return the first entry for pixel `i'. vtkPixelListEntry *GetFront(int i) { assert("pre: valid_i" && i>=0 && iGetSize()); assert("pre: not_empty" && this->GetListSize(i)>0); return this->Vector[i].GetFirst(); } // Remove the first entry for pixel `i'. void PopFront(int i, vtkPixelListEntryMemory *mm) { assert("pre: valid_i" && i>=0 && iGetSize()); assert("pre: not_empty" && this->GetListSize(i)>0); assert("pre: mm_exists" && mm!=0); this->Vector[i].RemoveFirst(mm); } // Return the begin iterator for pixel `i'. vtkPixelListEntry *GetFirst(int i) { assert("pre: valid_i" && i>=0 && iGetSize()); return this->Vector[i].GetFirst(); } #if 0 // Return the end iterator for pixel `i'. vtkstd::list::iterator GetEndIterator(int i) { assert("pre: valid_i" && i>=0 && iGetSize()); return this->Vector[i].end(); } #endif // Clear the list of each pixel of the frame. void Clean(vtkPixelListEntryMemory *mm) { assert("pre: mm_exists" && mm!=0); vtkIdType i=0; vtkIdType c=this->Vector.size(); while(iClear(mm); ++i; } } // Destructor. ~vtkPixelListFrame() { #if 0 vtkIdType i=0; vtkIdType c=this->Vector.size(); while(iempty()) { delete l->front(); l->pop_front(); } ++i; } #endif } vtkPixelList *GetList(int i) { assert("pre: valid_i" && i>=0 && iGetSize()); return &(this->Vector[i]); } protected: VectorType Vector; // the STL specification claims that // size() on a std: :list is permitted to be O(n)!!!! // vtkstd::vector Sizes; // vtkstd::list::iterator It; // vtkstd::list::iterator PreviousIt; // vtkstd::list::iterator ItEnd; }; //----------------------------------------------------------------------------- // Store a triangle face. Ids are in increasing order. Orientation does not // matter for the algorithm. class vtkFace { public: // Initialization from face ids in increasing order. vtkFace(vtkIdType faceIds[3]) { assert("pre: ordered ids" && faceIds[0]FaceIds[0]=faceIds[0]; this->FaceIds[1]=faceIds[1]; this->FaceIds[2]=faceIds[2]; this->Count=0; } // Return the 3 face ids. vtkIdType *GetFaceIds() { return this->FaceIds; } // Are `this' and faceIds equal? int IsEqual(vtkIdType faceIds[3]) { return (this->FaceIds[0]==faceIds[0])&&(this->FaceIds[1]==faceIds[1]) &&(this->FaceIds[2]==faceIds[2]); } void Ref() { ++this->Count; } void Unref() { --this->Count; if(this->Count==0) { delete this; } } int GetRendered() { return this->Rendered; } void SetRendered(int value) { this->Rendered=value; } protected: vtkIdType FaceIds[3]; int Count; int Rendered; private: vtkFace(); // not implemented vtkFace(const vtkFace &other); // not implemented vtkFace &operator=(const vtkFace &other); // not implemented }; //----------------------------------------------------------------------------- // For each vertex, store the list of faces incident on this vertex. // It is view independent. class vtkUseSet { public: typedef vtkstd::vector *> VectorType; VectorType Vector; vtkstd::list AllFaces; // to set up rendering to false. // Initialize with the number of vertices. vtkUseSet(int size) :Vector(size) { vtkIdType i=0; vtkIdType c=this->Vector.size(); while(iVector[i]=0; ++i; } } // Destructor. ~vtkUseSet() { vtkIdType i=0; vtkIdType c=this->Vector.size(); while(iVector[i]!=0) { while(!this->Vector[i]->empty()) { (*this->Vector[i]->begin())->Unref(); this->Vector[i]->pop_front(); } delete this->Vector[i]; } ++i; } while(!this->AllFaces.empty()) { (*this->AllFaces.begin())->Unref(); this->AllFaces.pop_front(); } } // For each vertex, clear the list of faces incident to it. void Clear() { vtkIdType i=0; vtkIdType c=this->Vector.size(); while(iVector[i]!=0) { while(!this->Vector[i]->empty()) { (*this->Vector[i]->begin())->Unref(); this->Vector[i]->pop_front(); } delete this->Vector[i]; this->Vector[i]=0; } ++i; } while(!this->AllFaces.empty()) { (*this->AllFaces.begin())->Unref(); this->AllFaces.pop_front(); } } // Add face to each vertex only if the useset does not have the face yet. void AddFace(vtkIdType faceIds[3]) { assert("pre: ordered ids" && faceIds[0]HasFace(faceIds)) { vtkFace *f=new vtkFace(faceIds); this->AllFaces.push_back(f); f->Ref(); // All the vertices of this face need to be fed int i=0; while(i<3) { vtkstd::list *p=this->Vector[faceIds[i]]; if(p==0) { p=new vtkstd::list; this->Vector[faceIds[i]]=p; } p->push_back(f); f->Ref(); ++i; } } } void SetNotRendered() { vtkstd::list::iterator it; vtkstd::list::iterator end; it=this->AllFaces.begin(); end=this->AllFaces.end(); while(it!=end) { (*it)->SetRendered(0); ++it; } } protected: // Does the use set of vertex faceIds[0] have face faceIds? int HasFace(vtkIdType faceIds[3]) { vtkstd::list *useSet=this->Vector[faceIds[0]]; int result=0; if(useSet!=0) { this->It=(*useSet).begin(); this->ItEnd=(*useSet).end(); while(!result && this->It!=this->ItEnd) { result=(*this->It)->IsEqual(faceIds); ++this->It; } } return result; } // Used in HasFace() vtkstd::list::iterator It; vtkstd::list::iterator ItEnd; }; // For each vertex, store its projection. It is view-dependent. class vtkVertices { public: typedef vtkstd::vector VectorType; VectorType Vector; // Initialize with the number of vertices. vtkVertices(int size) :Vector(size) { } }; //----------------------------------------------------------------------------- // Implementation of the public class. vtkCxxRevisionMacro(vtkUnstructuredGridVolumeZSweepMapper, "$Revision: 1.3 $"); vtkStandardNewMacro(vtkUnstructuredGridVolumeZSweepMapper); vtkCxxSetObjectMacro(vtkUnstructuredGridVolumeZSweepMapper, RayIntegrator, vtkUnstructuredGridVolumeRayIntegrator); //----------------------------------------------------------------------------- // Description: // Set MaxPixelListSize to 32. vtkUnstructuredGridVolumeZSweepMapper::vtkUnstructuredGridVolumeZSweepMapper() { this->MaxPixelListSize=64; // default value. this->ImageSampleDistance = 1.0; this->MinimumImageSampleDistance = 1.0; this->MaximumImageSampleDistance = 10.0; this->AutoAdjustSampleDistances = 1; this->ImageMemorySize[0] = 0; this->ImageMemorySize[1] = 0; this->Image = NULL; this->RealRGBAImage=0; this->RenderTimeTable = NULL; this->RenderVolumeTable = NULL; this->RenderRendererTable = NULL; this->RenderTableSize = 0; this->RenderTableEntries = 0; this->ZBuffer = NULL; this->ZBufferSize[0] = 0; this->ZBufferSize[1] = 0; this->ZBufferOrigin[0] = 0; this->ZBufferOrigin[1] = 0; this->IntermixIntersectingGeometry = 1; this->ImageDisplayHelper = vtkRayCastImageDisplayHelper::New(); this->ScalarMode = VTK_SCALAR_MODE_DEFAULT; this->ArrayName = new char[1]; this->ArrayName[0] = '\0'; this->ArrayId = -1; this->ArrayAccessMode = VTK_GET_ARRAY_BY_ID; this->PixelListFrame=0; this->Cell=vtkGenericCell::New(); this->EventList=vtkPriorityQueue::New(); this->UseSet=0; this->Vertices=0; this->PerspectiveTransform = vtkTransform::New(); this->PerspectiveMatrix = vtkMatrix4x4::New(); this->SimpleEdge=new vtkSimpleScreenEdge; this->DoubleEdge=new vtkDoubleScreenEdge; this->Span=new vtkSpan; this->RayIntegrator = NULL; this->RealRayIntegrator = NULL; this->IntersectionLengths=vtkDoubleArray::New(); this->IntersectionLengths->SetNumberOfValues(1); this->NearIntersections=vtkDoubleArray::New(); this->NearIntersections->SetNumberOfValues(1); this->FarIntersections=vtkDoubleArray::New(); this->FarIntersections->SetNumberOfValues(1); this->MemoryManager=0; } //----------------------------------------------------------------------------- vtkUnstructuredGridVolumeZSweepMapper::~vtkUnstructuredGridVolumeZSweepMapper() { if(this->MemoryManager!=0) { delete this->MemoryManager; } if(this->PixelListFrame!=0) { delete this->PixelListFrame; } this->Cell->Delete(); this->EventList->Delete(); this->ImageDisplayHelper->Delete(); delete[] this->ArrayName; if(this->UseSet!=0) { delete this->UseSet; } if(this->Vertices!=0) { delete this->Vertices; } this->PerspectiveTransform->Delete(); this->PerspectiveMatrix->Delete(); delete this->SimpleEdge; delete this->DoubleEdge; delete this->Span; if ( this->Image ) { delete [] this->Image; delete [] this->RealRGBAImage; } if ( this->RenderTableSize ) { delete [] this->RenderTimeTable; delete [] this->RenderVolumeTable; delete [] this->RenderRendererTable; } this->SetRayIntegrator(NULL); if (this->RealRayIntegrator) { this->RealRayIntegrator->UnRegister(this); } this->IntersectionLengths->Delete(); this->NearIntersections->Delete(); this->FarIntersections->Delete(); } //----------------------------------------------------------------------------- float vtkUnstructuredGridVolumeZSweepMapper::RetrieveRenderTime( vtkRenderer *ren, vtkVolume *vol ) { int i; for ( i = 0; i < this->RenderTableEntries; i++ ) { if ( this->RenderVolumeTable[i] == vol && this->RenderRendererTable[i] == ren ) { return this->RenderTimeTable[i]; } } return 0.0; } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::StoreRenderTime( vtkRenderer *ren, vtkVolume *vol, float time ) { int i; for ( i = 0; i < this->RenderTableEntries; i++ ) { if ( this->RenderVolumeTable[i] == vol && this->RenderRendererTable[i] == ren ) { this->RenderTimeTable[i] = time; return; } } // Need to increase size if ( this->RenderTableEntries >= this->RenderTableSize ) { if ( this->RenderTableSize == 0 ) { this->RenderTableSize = 10; } else { this->RenderTableSize *= 2; } float *oldTimePtr = this->RenderTimeTable; vtkVolume **oldVolumePtr = this->RenderVolumeTable; vtkRenderer **oldRendererPtr = this->RenderRendererTable; this->RenderTimeTable = new float [this->RenderTableSize]; this->RenderVolumeTable = new vtkVolume *[this->RenderTableSize]; this->RenderRendererTable = new vtkRenderer *[this->RenderTableSize]; for (i = 0; i < this->RenderTableEntries; i++ ) { this->RenderTimeTable[i] = oldTimePtr[i]; this->RenderVolumeTable[i] = oldVolumePtr[i]; this->RenderRendererTable[i] = oldRendererPtr[i]; } delete [] oldTimePtr; delete [] oldVolumePtr; delete [] oldRendererPtr; } this->RenderTimeTable[this->RenderTableEntries] = time; this->RenderVolumeTable[this->RenderTableEntries] = vol; this->RenderRendererTable[this->RenderTableEntries] = ren; this->RenderTableEntries++; } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::SelectScalarArray(int arrayNum) { if ( (this->ArrayId == arrayNum) && (this->ArrayAccessMode == VTK_GET_ARRAY_BY_ID) ) { return; } this->Modified(); this->ArrayId = arrayNum; this->ArrayAccessMode = VTK_GET_ARRAY_BY_ID; } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::SelectScalarArray( const char *arrayName) { if ( !arrayName || ( (strcmp(this->ArrayName, arrayName) == 0) && (this->ArrayAccessMode == VTK_GET_ARRAY_BY_ID) ) ) { return; } this->Modified(); delete[] this->ArrayName; this->ArrayName = new char[strlen(arrayName) + 1]; strcpy(this->ArrayName, arrayName); this->ArrayAccessMode = VTK_GET_ARRAY_BY_NAME; } //----------------------------------------------------------------------------- // Return the method for obtaining scalar data. const char *vtkUnstructuredGridVolumeZSweepMapper::GetScalarModeAsString(void) { if ( this->ScalarMode == VTK_SCALAR_MODE_USE_CELL_DATA ) { return "UseCellData"; } else if ( this->ScalarMode == VTK_SCALAR_MODE_USE_POINT_DATA ) { return "UsePointData"; } else if ( this->ScalarMode == VTK_SCALAR_MODE_USE_POINT_FIELD_DATA ) { return "UsePointFieldData"; } else if ( this->ScalarMode == VTK_SCALAR_MODE_USE_CELL_FIELD_DATA ) { return "UseCellFieldData"; } else { return "Default"; } } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "Max Pixel List Size: " << this->MaxPixelListSize << "\n"; os << indent << "ScalarMode: " << this->GetScalarModeAsString() << endl; os << indent << "Image Sample Distance: " << this->ImageSampleDistance << "\n"; os << indent << "Minimum Image Sample Distance: " << this->MinimumImageSampleDistance << "\n"; os << indent << "Maximum Image Sample Distance: " << this->MaximumImageSampleDistance << "\n"; os << indent << "Auto Adjust Sample Distances: " << this->AutoAdjustSampleDistances << "\n"; os << indent << "Intermix Intersecting Geometry: " << (this->IntermixIntersectingGeometry ? "On\n" : "Off\n"); // The PrintSelf test just search for words in the PrintSelf function // We add here the internal variable we don't want to display: // this->ImageViewportSize this->ImageOrigin this->ImageInUseSize os << indent << "ImageOrigin="<RayIntegrator) { os << indent << "RayIntegrator: " << this->RayIntegrator->GetClassName() << endl; } else { os << indent << "RayIntegrator: (automatic)" << endl; } } //----------------------------------------------------------------------------- // Description: // Maximum size allowed for a pixel list. Default is 32. // During the rendering, if a list of pixel is full, incremental compositing // is performed. Even if it is a user setting, it is an advanced parameter. // You have to understand how the algorithm works to change this value. int vtkUnstructuredGridVolumeZSweepMapper::GetMaxPixelListSize() { return this->MaxPixelListSize; } //----------------------------------------------------------------------------- // Description: // Change the maximum size allowed for a pixel list. It is an advanced // parameter. void vtkUnstructuredGridVolumeZSweepMapper::SetMaxPixelListSize(int size) { assert("pre: positive_size" && size>0); this->MaxPixelListSize=size; } //----------------------------------------------------------------------------- #define ESTABLISH_INTEGRATOR(classname) \ if ( !this->RealRayIntegrator \ || (!this->RealRayIntegrator->IsA(#classname)) ) \ { \ if (this->RealRayIntegrator) this->RealRayIntegrator->UnRegister(this); \ this->RealRayIntegrator = classname::New(); \ this->RealRayIntegrator->Register(this); \ this->RealRayIntegrator->Delete(); \ } \ //----------------------------------------------------------------------------- // Description: // WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE // DO NOT USE THIS METHOD OUTSIDE OF THE RENDERING PROCESS // Render the volume void vtkUnstructuredGridVolumeZSweepMapper::Render(vtkRenderer *ren, vtkVolume *vol) { vtkDebugMacro(<<"Render"); // Check for input if(this->GetInput()==0) { vtkErrorMacro(<< "No Input!"); return; } this->Scalars = this->GetScalars(this->GetInput(), this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, this->CellScalars); if(this->Scalars==0) { vtkErrorMacro("Can't use the ZSweep mapper without scalars!"); return; } this->GetInput()->UpdateInformation(); this->GetInput()->SetUpdateExtentToWholeExtent(); this->GetInput()->Update(); // Check to make sure we have an appropriate integrator. if (this->RayIntegrator) { if (this->RealRayIntegrator != this->RayIntegrator) { if (this->RealRayIntegrator) { this->RealRayIntegrator->UnRegister(this); } this->RealRayIntegrator = this->RayIntegrator; this->RealRayIntegrator->Register(this); } } else { if (this->CellScalars) { ESTABLISH_INTEGRATOR(vtkUnstructuredGridHomogeneousRayIntegrator); } else { if (vol->GetProperty()->GetIndependentComponents()) { ESTABLISH_INTEGRATOR(vtkUnstructuredGridPreIntegration); } else { ESTABLISH_INTEGRATOR(vtkUnstructuredGridPartialPreIntegration); } } } // Start timing now. We didn't want to capture the update of the // input data in the times this->Timer->StartTimer(); int oldImageMemorySize[2]; oldImageMemorySize[0] = this->ImageMemorySize[0]; oldImageMemorySize[1] = this->ImageMemorySize[1]; // If we are automatically adjusting the size to achieve a desired frame // rate, then do that adjustment here. Base the new image sample distance // on the previous one and the previous render time. Don't let // the adjusted image sample distance be less than the minimum image sample // distance or more than the maximum image sample distance. float oldImageSampleDistance = this->ImageSampleDistance; if ( this->AutoAdjustSampleDistances ) { float oldTime = this->RetrieveRenderTime( ren, vol ); float newTime = vol->GetAllocatedRenderTime(); this->ImageSampleDistance *= sqrt(oldTime / newTime); this->ImageSampleDistance = (this->ImageSampleDistance>this->MaximumImageSampleDistance)? (this->MaximumImageSampleDistance):(this->ImageSampleDistance); this->ImageSampleDistance = (this->ImageSampleDistanceMinimumImageSampleDistance)? (this->MinimumImageSampleDistance):(this->ImageSampleDistance); } // The full image fills the viewport. First, compute the actual viewport // size, then divide by the ImageSampleDistance to find the full image // size in pixels int width, height; ren->GetTiledSize(&width, &height); this->ImageViewportSize[0] = static_cast(width/this->ImageSampleDistance); this->ImageViewportSize[1] = static_cast(height/this->ImageSampleDistance); this->ImageInUseSize[0] = this->ImageViewportSize[0]; this->ImageInUseSize[1] = this->ImageViewportSize[1]; this->ImageOrigin[0] = 0; this->ImageOrigin[1] = 0; // What is a power of 2 size big enough to fit this image? this->ImageMemorySize[0] = 32; this->ImageMemorySize[1] = 32; while ( this->ImageMemorySize[0] < this->ImageInUseSize[0] ) { this->ImageMemorySize[0] *= 2; } while ( this->ImageMemorySize[1] < this->ImageInUseSize[1] ) { this->ImageMemorySize[1] *= 2; } // If the old image size is much too big (more than twice in // either direction) then set the old width to 0 which will // cause the image to be recreated if ( oldImageMemorySize[0] > 2*this->ImageMemorySize[0] || oldImageMemorySize[1] > 2*this->ImageMemorySize[1] ) { oldImageMemorySize[0] = 0; } // If the old image is big enough (but not too big - we handled // that above) then we'll bump up our required size to the // previous one. This will keep us from thrashing. if ( oldImageMemorySize[0] >= this->ImageMemorySize[0] && oldImageMemorySize[1] >= this->ImageMemorySize[1] ) { this->ImageMemorySize[0] = oldImageMemorySize[0]; this->ImageMemorySize[1] = oldImageMemorySize[1]; } int bufferSize=this->ImageMemorySize[0] * this->ImageMemorySize[1] * 4; // Do we already have a texture big enough? If not, create a new one and // clear it. if ( !this->Image || this->ImageMemorySize[0] > oldImageMemorySize[0] || this->ImageMemorySize[1] > oldImageMemorySize[1] ) { // If there is an image there must be row bounds if ( this->Image ) { delete [] this->Image; delete [] this->RealRGBAImage; } this->Image = new unsigned char[bufferSize]; this->RealRGBAImage=new float[bufferSize]; } // We have to clear the image, each time: memset(this->Image,0,bufferSize); vtkIdType j=0; while(jRealRGBAImage[j]=0; this->RealRGBAImage[j+1]=0; this->RealRGBAImage[j+2]=0; this->RealRGBAImage[j+3]=0; j+=4; } // Capture the zbuffer if necessary if ( this->IntermixIntersectingGeometry && ren->GetNumberOfPropsRendered() ) { int x1, x2, y1, y2; double *viewport = ren->GetViewport(); int *renWinSize = ren->GetRenderWindow()->GetSize(); // turn this->ImageOrigin into (x1,y1) in window (not viewport!) // coordinates. x1 = static_cast ( viewport[0] * static_cast(renWinSize[0]) + static_cast(this->ImageOrigin[0]) * this->ImageSampleDistance ); y1 = static_cast ( viewport[1] * static_cast(renWinSize[1]) + static_cast(this->ImageOrigin[1]) * this->ImageSampleDistance); // compute z buffer size this->ZBufferSize[0] = static_cast( static_cast(this->ImageInUseSize[0]) * this->ImageSampleDistance); this->ZBufferSize[1] = static_cast( static_cast(this->ImageInUseSize[1]) * this->ImageSampleDistance); // Use the size to compute (x2,y2) in window coordinates x2 = x1 + this->ZBufferSize[0] - 1; y2 = y1 + this->ZBufferSize[1] - 1; // This is the z buffer origin (in viewport coordinates) this->ZBufferOrigin[0] = static_cast( static_cast(this->ImageOrigin[0]) * this->ImageSampleDistance); this->ZBufferOrigin[1] = static_cast( static_cast(this->ImageOrigin[1]) * this->ImageSampleDistance); // Capture the z buffer this->ZBuffer = ren->GetRenderWindow()->GetZbufferData(x1,y1,x2,y2); } this->RealRayIntegrator->Initialize(vol, this->Scalars); // Here is the Zsweep algorithm: // 1. For each vertex, find the list of incident faces (the "use set") (3.1) // In the original paper, it deals with incident cells but the chapter about // the parallel version in the dissertation deals with faces, which makes // more sense. Hence, there is no need for the sparsification step (3.5.1) // It is view-independent, so it can be reused for the next call to Render() // if the dataset did not change. vtkDebugMacro(<<"BuildUseSets: start"); this->BuildUseSets(); vtkDebugMacro(<<"BuildUseSets: done"); // 2. Sort the vertices by z-coordinates (view-dependent) in view space. // For each vertex, compute its camera coordinates and sort it // by z in an heap. The heap is called the "event list". // The heap stores the Id of the vertices. // It is view-dependent. vtkDebugMacro(<<"ProjectAndSortVertices: start"); this->ProjectAndSortVertices(ren,vol); vtkDebugMacro(<<"ProjectAndSortVertices: done"); // 3. Create an empty "pixel list" (two way linked list) for each pixel of // the screen. vtkDebugMacro(<<"CreateAndCleanPixelList: start"); this->CreateAndCleanPixelList(); vtkDebugMacro(<<"CreateAndCleanPixelList: done"); // 4. Main loop // (section 2 paragraph 11) vtkDebugMacro(<<"MainLoop: start"); this->MainLoop(ren->GetRenderWindow()); vtkDebugMacro(<<"MainLoop: done"); // The algorithm is done: send to result to the final image. if ( !ren->GetRenderWindow()->GetAbortRender() ) { float depth; if ( this->IntermixIntersectingGeometry ) { depth = this->GetMinimumBoundsDepth( ren, vol ); } else { depth = -1; } // copy the double image into the unsigned char image: j=0; while(jRealRGBAImage[j+3]; if(alpha!=0) { this->Image[j]=this->ColorComponentRealToByte(this->RealRGBAImage[j]/alpha); this->Image[j+1]=this->ColorComponentRealToByte(this->RealRGBAImage[j+1]/alpha); this->Image[j+2]=this->ColorComponentRealToByte(this->RealRGBAImage[j+2]/alpha); this->Image[j+3]=this->ColorComponentRealToByte(alpha); } else { this->Image[j]=0; this->Image[j+1]=0; this->Image[j+2]=0; this->Image[j+3]=0; } j+=4; } this->ImageDisplayHelper-> RenderTexture( vol, ren, this->ImageMemorySize, this->ImageViewportSize, this->ImageInUseSize, this->ImageOrigin, depth, this->Image ); this->Timer->StopTimer(); this->TimeToDraw = this->Timer->GetElapsedTime(); this->StoreRenderTime( ren, vol, this->TimeToDraw ); } else { this->ImageSampleDistance = oldImageSampleDistance; } if ( this->ZBuffer ) { delete [] this->ZBuffer; this->ZBuffer = NULL; } this->UpdateProgress(1.0); } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::AllocateUseSet(vtkIdType size) { if(this->UseSet!=0) { if(size>static_cast(this->UseSet->Vector.size())) { delete this->UseSet; this->UseSet=new vtkUseSet(size); } else { this->UseSet->Clear(); } } else { this->UseSet=new vtkUseSet(size); } } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::AllocateVertices(vtkIdType size) { if(this->Vertices!=0) { if(size>static_cast(this->Vertices->Vector.size())) { delete this->Vertices; this->Vertices=new vtkVertices(size); } } else { this->Vertices=new vtkVertices(size); } } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::BuildUseSets() { int needsUpdate = 0; // If we have never created the list, we need updating if (this->UseSet==0 ) { needsUpdate = 1; } // If the data has changed in some way then we need to update vtkUnstructuredGrid *input = this->GetInput(); if ( input->GetMTime() > this->SavedTriangleListMTime.GetMTime() ) { needsUpdate = 1; } // If we don't need updating, return if ( !needsUpdate ) { return; } vtkIdType numberOfCells=input->GetNumberOfCells(); vtkIdType numberOfPoints=input->GetNumberOfPoints(); // init the use set of each vertex this->AllocateUseSet(numberOfPoints); // for each cell vtkIdType cellIdx=0; while(cellIdxGetCell(cellIdx,this->Cell); vtkIdType faces=this->Cell->GetNumberOfFaces(); vtkIdType faceidx=0; vtkCell *face; vtkIdType faceIds[3]; vtkIdType orderedFaceIds[3]; // for each face while(faceidxCell->GetFace(faceidx); faceIds[0]=face->GetPointId(0); faceIds[1]=face->GetPointId(1); faceIds[2]=face->GetPointId(2); this->ReorderTriangle(faceIds,orderedFaceIds); // Add face only if it is not already in the useset. this->UseSet->AddFace(orderedFaceIds); ++faceidx; } ++cellIdx; } this->SavedTriangleListMTime.Modified(); } //----------------------------------------------------------------------------- // Description: // Reorder vertices `v' in increasing order in `w'. Orientation does not // matter for the algorithm. void vtkUnstructuredGridVolumeZSweepMapper::ReorderTriangle(vtkIdType v[3], vtkIdType w[3]) { if(v[0]>v[1]) { if(v[1]>v[2]) { // v[2] is the min w[0]=v[2]; w[1]=v[0]; w[2]=v[1]; } else { // v[1] is the min w[0]=v[1]; w[1]=v[2]; w[2]=v[0]; } } else { if(v[0]>v[2]) { // v[2] is the min w[0]=v[2]; w[1]=v[0]; w[2]=v[1]; } else { // v[0] is the min w[0]=v[0]; w[1]=v[1]; w[2]=v[2]; } } // At this point the triangle start with the min id and the // order did not change // Now, ensure that the two last id are in increasing order if(w[1]>w[2]) { vtkIdType tmp=w[1]; w[1]=w[2]; w[2]=tmp; } } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::ProjectAndSortVertices( vtkRenderer *ren, vtkVolume *vol) { assert("pre: empty list" && this->EventList->GetNumberOfItems()==0); vtkUnstructuredGrid *input = this->GetInput(); vtkIdType numberOfPoints=input->GetNumberOfPoints(); vtkIdType pointId=0; vtkVertexEntry *vertex=0; // Pre-computation for the projection. ren->ComputeAspect(); double *aspect = ren->GetAspect(); // Get the view matrix in two steps - there is a one step method in camera // but it turns off stereo so we do not want to use that one vtkCamera *cam = ren->GetActiveCamera(); this->PerspectiveTransform->Identity(); this->PerspectiveTransform->Concatenate( cam->GetPerspectiveTransformMatrix(aspect[0]/aspect[1], 0.0, 1.0 )); this->PerspectiveTransform->Concatenate(cam->GetViewTransformMatrix()); this->PerspectiveTransform->Concatenate(vol->GetMatrix()); this->PerspectiveMatrix->DeepCopy(this->PerspectiveTransform->GetMatrix()); this->AllocateVertices(numberOfPoints); while(pointIdVertices->Vector[pointId]); // Projection // double inPoint[4]; input->GetPoint(pointId,inPoint); inPoint[3] = 1.0; double outPoint[4]; this->PerspectiveMatrix->MultiplyPoint( inPoint, outPoint ); assert("outPoint[3]" && outPoint[3]!=0.0); double invW=1/outPoint[3]; double zView = outPoint[2]*invW; int xScreen=static_cast((outPoint[0]*invW+1)*0.5*this->ImageViewportSize[0]-this->ImageOrigin[0]); int yScreen=static_cast((outPoint[1]*invW+1)*0.5*this->ImageViewportSize[1]-this->ImageOrigin[1]); double outWorldPoint[4]; vol->GetMatrix()->MultiplyPoint( inPoint, outWorldPoint ); assert("check: vol no projection" && outWorldPoint[3]==1); double scalar; if(this->CellScalars) // cell attribute { assert(0); // scalar=this->Scalars->GetComponent(cellIdx,0); scalar=0; } else // point attribute { scalar=this->Scalars->GetComponent(pointId,0); } vertex->Set(xScreen,yScreen,outWorldPoint[0]/outWorldPoint[3], outWorldPoint[1]/outWorldPoint[3], outWorldPoint[2]/outWorldPoint[3],zView,scalar,invW); // Sorting // // we store -z because the top of the priority list is the // smallest value #ifdef BACK_TO_FRONT this->EventList->Insert(-zView,pointId); #else this->EventList->Insert(zView,pointId); #endif ++pointId; } } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::CreateAndCleanPixelList() { // paper: a "pixel list" is a double linked list. We put that in a queue. vtkIdType size=this->ImageInUseSize[0]*this->ImageInUseSize[1]; if(this->PixelListFrame!=0) { if(this->PixelListFrame->GetSize()PixelListFrame; this->PixelListFrame=0; } } if(this->PixelListFrame==0) { this->PixelListFrame=new vtkPixelListFrame(size); } } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::MainLoop(vtkRenderWindow *renWin) { double previousZTarget=0.0; double zTarget; vtkIdType vertex; // used to know if the next vertex is on the same plane double currentZ; // than the previous one. If so, the z-target has to be // updated (without calling the compositing function) if(this->EventList->GetNumberOfItems()==0) { return; // we are done. } // initialize the "previous z-target" to the z-coordinate of the first // vertex. vertex=this->EventList->Peek(0,previousZTarget); #ifdef BACK_TO_FRONT previousZTarget=-previousZTarget; // because the EventList store -z #endif // (section 2 paragraph 11) // initialize the "z-target" with the maximum z-coordinate of the adjacent // vertices to the first vertex. The adjacent vertices can be found // indirectly by using the "use set" of the first vertex (cells), and // by taking the vertices of all those cells. // zTarget=previousZTarget; vtkstd::list::iterator it; vtkstd::list::iterator itEnd; // this->MaxRecordedPixelListSize=0; this->MaxPixelListSizeReached=0; this->XBounds[0]=this->ImageInUseSize[0]; this->XBounds[1]=0; this->YBounds[0]=this->ImageInUseSize[1]; this->YBounds[1]=0; vtkIdType progressCount=0; vtkIdType sum=this->EventList->GetNumberOfItems(); if(this->MemoryManager==0) { this->MemoryManager=new vtkPixelListEntryMemory; } this->UseSet->SetNotRendered(); int aborded=0; // for each vertex of the "event list" while(this->EventList->GetNumberOfItems()>0) { this->UpdateProgress(static_cast(progressCount)/sum); aborded=renWin->CheckAbortStatus(); if(aborded) { break; } ++progressCount; // the z coordinate of the current vertex defines the "sweep plane". vertex=this->EventList->Pop(0,currentZ); if(this->UseSet->Vector[vertex]!=0) { // otherwise the vertex is not useful, basically this is the // end we reached the last ztarget #ifdef BACK_TO_FRONT currentZ=-currentZ; // because the EventList store -z #endif if(previousZTarget==currentZ) { // the new vertex is on the same sweep plane than the previous vertex // that defined a z target // => the z target has to be updated accordingly // This is also the case for the first vertex. it=this->UseSet->Vector[vertex]->begin(); itEnd=this->UseSet->Vector[vertex]->end(); // for each face incident with the vertex while(it!=itEnd) { vtkFace *face=(*it); // for each point of the face, get the closest z vtkIdType *vids=face->GetFaceIds(); vtkIdType i=0; while(i<3) { double z=this->Vertices->Vector[vids[i]].GetZview(); #ifdef BACK_TO_FRONT if(zzTarget) #endif { zTarget=z; } ++i; } ++it; } } // Time to call the composite function? #ifdef BACK_TO_FRONT if(currentZzTarget) #endif { this->CompositeFunction(zTarget); // Update the zTarget previousZTarget=zTarget; it=this->UseSet->Vector[vertex]->begin(); itEnd=this->UseSet->Vector[vertex]->end(); // for each cell incident with the vertex while(it!=itEnd) { vtkFace *face=(*it); // for each point of the face, get the closest z vtkIdType *vids=face->GetFaceIds(); vtkIdType i=0; while(i<3) { double z=this->Vertices->Vector[vids[i]].GetZview(); #ifdef BACK_TO_FRONT if(zzTarget) #endif { zTarget=z; } ++i; } ++it; } } else { if(this->MaxPixelListSizeReached) { this->CompositeFunction(currentZ); // We do not update the zTarget in this case. } } // use the "use set" (cells) of the vertex to get the cells that are // incident on the vertex, and that have this vertex as // minimal z-coordinate, it=this->UseSet->Vector[vertex]->begin(); itEnd=this->UseSet->Vector[vertex]->end(); while(it!=itEnd) { vtkFace *face=(*it); if(!face->GetRendered()) { vtkIdType *vids=face->GetFaceIds(); this->RasterizeFace(vids); face->SetRendered(1); } #if 0 // face search // for each point of the face, get the closest z vtkIdType *vids=face->GetFaceIds(); vtkIdType minVertex=vids[0]; double farestZ=this->Vertices->Vector[vids[0]].GetZview(); vtkIdType i=1; while(i<3) { double z=this->Vertices->Vector[vids[i]].GetZview(); #ifdef BACK_TO_FRONT if(z>farestZ) #else if(zGetRendered()) // { // cout<<"FACE ALREADY RENDERED!!!!"<RasterizeFace(vids); // face->SetRendered(1); } #endif // face search ++it; } } // if useset of vertex is not null } // while(eventList->GetNumberOfItems()>0) if(!aborded) { // Here a final compositing vtkDebugMacro(<<"Flush Compositing"); // this->SavePixelListFrame(); #ifdef BACK_TO_FRONT this->CompositeFunction(-2); #else this->CompositeFunction(2); #endif } else { this->EventList->Reset(); } this->PixelListFrame->Clean(this->MemoryManager); // vtkDebugMacro(<<"MaxRecordedPixelListSize="<MaxRecordedPixelListSize); assert("post: empty_list" && this->EventList->GetNumberOfItems()==0); } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::SavePixelListFrame() { vtkPolyData *dataset=vtkPolyData::New(); vtkIdType height=this->ImageInUseSize[1]; vtkIdType width=this->ImageInUseSize[0]; vtkPixelListEntry *current; vtkIdType i; vtkPoints *pts=vtkPoints::New(); pts->SetDataTypeToDouble(); vtkDoubleArray *dataArray=vtkDoubleArray::New(); vtkCellArray *vertices=vtkCellArray::New(); vtkIdType pointId=0; // height=151; // width=151; vtkIdType y=0; //150; while(yImageInUseSize[0]+x; current=this->PixelListFrame->GetFirst(i); while(current!=0) { double *values=current->GetValues(); double point[3]; point[0]=x; point[1]=y; point[2]=values[2]; // zWorld pts->InsertNextPoint(point); dataArray->InsertNextValue(values[3]); vertices->InsertNextCell(1,&pointId); current=current->GetNext(); ++pointId; } ++x; } ++y; } dataset->SetPoints(pts); pts->Delete(); dataset->SetVerts(vertices); vertices->Delete(); dataset->GetPointData()->SetScalars(dataArray); dataArray->Delete(); vtkXMLPolyDataWriter *writer=vtkXMLPolyDataWriter::New(); writer->SetFileName("pixellistframe.vtp"); writer->SetInput(dataset); writer->SetIdTypeToInt32(); dataset->Delete(); writer->Write(); writer->Delete(); } //----------------------------------------------------------------------------- // Description: // Perform a scan conversion of a triangle, interpolating z and the scalar. void vtkUnstructuredGridVolumeZSweepMapper::RasterizeFace(vtkIdType faceIds[3]) { // The triangle is splitted by an horizontal line passing through the // second vertex v1 (y-order) // Hence, on one side there one edge (v0v2), on the other side there are two // edges (v0v1 and v1v2). vtkVertexEntry *v0=&(this->Vertices->Vector[faceIds[0]]); vtkVertexEntry *v1=&(this->Vertices->Vector[faceIds[1]]); vtkVertexEntry *v2=&(this->Vertices->Vector[faceIds[2]]); this->RasterizeTriangle(v0,v1,v2); } //----------------------------------------------------------------------------- // Description: // Perform a scan conversion of a triangle, interpolating z and the scalar. void vtkUnstructuredGridVolumeZSweepMapper::RasterizeTriangle( vtkVertexEntry *ve0, vtkVertexEntry *ve1, vtkVertexEntry *ve2 ) { assert("pre: ve0_exists" && ve0!=0); assert("pre: ve1_exists" && ve1!=0); assert("pre: ve2_exists" && ve2!=0); vtkVertexEntry *v0=ve0; vtkVertexEntry *v1=ve1; vtkVertexEntry *v2=ve2; // The triangle is splitted by an horizontal line passing through the // second vertex v1 (y-order) // Hence, on one side there one edge (v0v2), on the other side there are two // edges (v0v1 and v1v2). // Order vertices by y screen. vtkVertexEntry *tmp; if(v0->GetScreenY()>v1->GetScreenY()) { tmp=v0; v0=v1; v1=tmp; } if(v0->GetScreenY()>v2->GetScreenY()) { tmp=v1; v1=v0; v0=v2; v2=tmp; } else { if(v1->GetScreenY()>v2->GetScreenY()) { tmp=v1; v1=v2; v2=tmp; } } if(v0->GetScreenY()YBounds[0]) { if(v0->GetScreenY()>=0) { this->YBounds[0]=v0->GetScreenY(); } else { this->YBounds[0]=0; } } if(v2->GetScreenY()>this->YBounds[1]) { if(v2->GetScreenY()ImageInUseSize[1]) { this->YBounds[1]=v2->GetScreenY(); } else { this->YBounds[1]=this->ImageInUseSize[1]-1; } } int x=v0->GetScreenX(); if(xXBounds[0]) { if(x>=0) { this->XBounds[0]=x; } else { this->XBounds[0]=0; } } else { if(x>this->XBounds[1]) { if(xImageInUseSize[0]) { this->XBounds[1]=x; } else { this->XBounds[1]=this->ImageInUseSize[0]-1; } } } x=v1->GetScreenX(); if(xXBounds[0]) { if(x>=0) { this->XBounds[0]=x; } else { this->XBounds[0]=0; } } else { if(x>this->XBounds[1]) { if(xImageInUseSize[0]) { this->XBounds[1]=x; } else { this->XBounds[1]=this->ImageInUseSize[0]-1; } } } x=v2->GetScreenX(); if(xXBounds[0]) { if(x>=0) { this->XBounds[0]=x; } else { this->XBounds[0]=0; } } else { if(x>this->XBounds[1]) { if(xImageInUseSize[0]) { this->XBounds[1]=x; } else { this->XBounds[1]=this->ImageInUseSize[0]-1; } } } int dy20=v2->GetScreenY()-v0->GetScreenY(); int dx10=v1->GetScreenX()-v0->GetScreenX(); int dx20=v2->GetScreenX()-v0->GetScreenX(); int dy10=v1->GetScreenY()-v0->GetScreenY(); int det=dy20*dx10-dx20*dy10; vtkScreenEdge *leftEdge=0; vtkScreenEdge *rightEdge=0; if(det==0) //v0v1v2 aligned or v0=v1=v2 { // easy case: v0=v1=v2 render the 3 points if(v0->GetScreenX()==v1->GetScreenX() && v0->GetScreenX()==v2->GetScreenX() && v0->GetScreenY()==v1->GetScreenY() && v0->GetScreenY()==v2->GetScreenY()) { x=v0->GetScreenX(); int y=v0->GetScreenY(); if(x>=0 && xImageInUseSize[0] && y>=0 && yImageInUseSize[1]) { vtkIdType i=y*this->ImageInUseSize[0]+x; // Write the pixel vtkPixelListEntry *p0=this->MemoryManager->AllocateEntry(); p0->Init(v0->GetValues(),v0->GetZview()); this->PixelListFrame->AddAndSort(i,p0); vtkPixelListEntry *p1=this->MemoryManager->AllocateEntry(); p1->Init(v1->GetValues(),v1->GetZview()); this->PixelListFrame->AddAndSort(i,p1); vtkPixelListEntry *p2=this->MemoryManager->AllocateEntry(); p2->Init(v2->GetValues(),v2->GetZview()); this->PixelListFrame->AddAndSort(i,p2); // if(this->PixelListFrame->GetListSize(i)>this->MaxRecordedPixelListSize) // { // this->MaxRecordedPixelListSize=this->PixelListFrame->GetListSize(i); // } if(!this->MaxPixelListSizeReached) { this->MaxPixelListSizeReached=this->PixelListFrame->GetListSize(i)> this->MaxPixelListSize; } } } else // line { this->RasterizeLine(v0,v1); this->RasterizeLine(v1,v2); this->RasterizeLine(v0,v2); } return; } else { if(det>0) //v0v1 on right { this->DoubleEdge->Init(v0,v1,v2,dx10,dy10,1); // true=on right rightEdge=this->DoubleEdge; this->SimpleEdge->Init(v0,v2,dx20,dy20,0); leftEdge=this->SimpleEdge; } else { // v0v1 on left this->DoubleEdge->Init(v0,v1,v2,dx10,dy10,0); // true=on right leftEdge=this->DoubleEdge; this->SimpleEdge->Init(v0,v2,dx20,dy20,1); rightEdge=this->SimpleEdge; } } int y=v0->GetScreenY(); int y1=v1->GetScreenY(); int y2=v2->GetScreenY(); int skipped=0; if(y1>=0) // clipping { if(y1>=this->ImageInUseSize[1]) // clipping { y1=this->ImageInUseSize[1]-1; } while(y<=y1) { if(y>=0 && yImageInUseSize[1]) // clipping { this->RasterizeSpan(y,leftEdge,rightEdge); } ++y; if(y<=y1) { leftEdge->NextLine(y); rightEdge->NextLine(y); } } } else { leftEdge->SkipLines(y1-y,y1); rightEdge->SkipLines(y1-y,y1); y=y1; skipped=1; } if(yImageInUseSize[1]) // clipping { leftEdge->OnBottom(skipped,y); rightEdge->OnBottom(skipped,y); if(y2>=this->ImageInUseSize[1]) // clipping { y2=this->ImageInUseSize[1]-1; } while(y<=y2) { if(y>=0) // clipping, needed in case of no top { this->RasterizeSpan(y,leftEdge,rightEdge); } ++y; leftEdge->NextLine(y); rightEdge->NextLine(y); } } } //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::RasterizeSpan(int y, vtkScreenEdge *left, vtkScreenEdge *right) { assert("pre: left_exists" && left!=0); assert("pre: right_exists" && right!=0); vtkIdType i=y*this->ImageInUseSize[0]; this->Span->Init(left->GetX(), left->GetInvW(), left->GetPValues(), left->GetZview(), right->GetX(), right->GetInvW(), right->GetPValues(), right->GetZview()); while(!this->Span->IsAtEnd()) { int x=this->Span->GetX(); if(x>=0 && xImageInUseSize[0]) // clipping { vtkIdType j=i+x; // Write the pixel vtkPixelListEntry *p=this->MemoryManager->AllocateEntry(); p->Init(this->Span->GetValues(),this->Span->GetZview()); this->PixelListFrame->AddAndSort(j,p); // if(this->PixelListFrame->GetListSize(j)>this->MaxRecordedPixelListSize) // { // this->MaxRecordedPixelListSize=this->PixelListFrame->GetListSize(j); // } if(!this->MaxPixelListSizeReached) { this->MaxPixelListSizeReached=this->PixelListFrame->GetListSize(j)> this->MaxPixelListSize; } } this->Span->NextPixel(); } } enum { VTK_LINE_CONSTANT=0, VTK_LINE_BRESENHAM, VTK_LINE_DIAGONAL }; //----------------------------------------------------------------------------- void vtkUnstructuredGridVolumeZSweepMapper::RasterizeLine(vtkVertexEntry *v0, vtkVertexEntry *v1) { assert("pre: v0_exists" && v0!=0); assert("pre: v1_exists" && v1!=0); assert("pre: y_ordered" && v0->GetScreenY()<=v1->GetScreenY()); int lineCase; int xIncrement; // if true increment x, if false increment y int dx; int dy; int xSign; // initialization is not useful, it is just to remove compiler warnings int dx2=0; int dy2=0; int e=0; double values[VTK_VALUES_SIZE]; double pValues[VTK_VALUES_SIZE]; double dPv[VTK_VALUES_SIZE]; double dInvW; double dZ; double zView; double invW; int i; int x=v0->GetScreenX(); int y=v0->GetScreenY(); // 1. Find the case dx=v1->GetScreenX()-v0->GetScreenX(); if(dx<0) { dx=-dx; xSign=-1; } else { xSign=1; } dy=v1->GetScreenY()-v0->GetScreenY(); xIncrement=dx>dy; if(xIncrement) { if(dy==0) { lineCase=VTK_LINE_CONSTANT; } else { lineCase=VTK_LINE_BRESENHAM; dx2=dx<<1; dy2=dy<<1; e=dx; } double invDx=1.0/dx; i=0; invW=v0->GetInvW(); double invW1=v1->GetInvW(); double *val0=v0->GetValues(); double *val1=v1->GetValues(); while(iGetZview(); dZ=(v1->GetZview()-zView)*invDx; } else { if(dx==0) { if(dy==0) { // render both points and return // write pixel if(x>=0 && xImageInUseSize[0] && y>=0 && yImageInUseSize[1]) // clipping { vtkIdType j=y*this->ImageInUseSize[0]+x; // mult==bad!! // Write the pixel vtkPixelListEntry *p0=this->MemoryManager->AllocateEntry(); p0->Init(v0->GetValues(),v0->GetZview()); this->PixelListFrame->AddAndSort(j,p0); // Write the pixel vtkPixelListEntry *p1=this->MemoryManager->AllocateEntry(); p1->Init(v1->GetValues(),v1->GetZview()); this->PixelListFrame->AddAndSort(j,p1); if(!this->MaxPixelListSizeReached) { this->MaxPixelListSizeReached=this->PixelListFrame->GetListSize(j)> this->MaxPixelListSize; } } return; } else { lineCase=VTK_LINE_CONSTANT; } } else { if(dy==dx) { lineCase=VTK_LINE_DIAGONAL; } else { lineCase=VTK_LINE_BRESENHAM; dx2=dx<<1; dy2=dy<<1; e=dy; } } double invDy=1.0/dy; i=0; invW=v0->GetInvW(); double invW1=v1->GetInvW(); double *val0=v0->GetValues(); double *val1=v1->GetValues(); while(iGetZview(); dZ=(v1->GetZview()-zView)*invDy; } // 2. Iterate over each pixel of the straight line. int done=0; while(!done) { // write pixel if(x>=0 && xImageInUseSize[0] && y>=0 && yImageInUseSize[1]) // clipping { vtkIdType j=y*this->ImageInUseSize[0]+x; // mult==bad!! // Write the pixel vtkPixelListEntry *p0=this->MemoryManager->AllocateEntry(); p0->Init(values,zView); this->PixelListFrame->AddAndSort(j,p0); if(!this->MaxPixelListSizeReached) { this->MaxPixelListSizeReached=this->PixelListFrame->GetListSize(j)> this->MaxPixelListSize; } } // next pixel switch(lineCase) { case VTK_LINE_CONSTANT: if(xIncrement) { x+=xSign; if(xSign>0) { done=x>v1->GetScreenX(); } else { done=xGetScreenX(); } } else { ++y; done=y>v1->GetScreenY(); } // values, invw, zview break; case VTK_LINE_DIAGONAL: ++y; x+=xSign; done=y>v1->GetScreenY(); // values, invw, zview break; case VTK_LINE_BRESENHAM: if(xIncrement) { x+=xSign; e+=dy2; if(e>=dx2) { e-=dx2; ++y; } if(xSign>0) { done=x>v1->GetScreenX(); } else { done=xGetScreenX(); } } else { ++y; e+=dx2; if(e>=dy2) { e-=dy2; x+=xSign; } done=y>v1->GetScreenY(); } // values, invw, zview break; } if(!done) { invW+=dInvW; i=0; double w=1.0/invW; while(iYBounds[0]; vtkIdType i=y*this->ImageInUseSize[0]+this->XBounds[0]; vtkIdType index=(y*this->ImageMemorySize[0]+this->XBounds[0])<< 2; // *4 vtkIdType indexStep=this->ImageMemorySize[0]<<2; // *4 vtkPixelListEntry *current; vtkPixelListEntry *next; double zBuffer=0; int newXBounds[2]; int newYBounds[2]; newXBounds[0]=this->ImageInUseSize[0]; newXBounds[1]=0; newYBounds[0]=this->ImageInUseSize[1]; newYBounds[1]=0; int xMin=this->XBounds[0]; int xMax=this->XBounds[1]; int yMax=this->YBounds[1]; vtkPixelList *pixel; int x; vtkIdType j; vtkIdType index2; int done; int doIntegration; double length; float *color; // for each pixel in the bounding box while(y<=yMax) { x=xMin; j=i; index2=index; while(x<=xMax) { pixel=this->PixelListFrame->GetList(j); // we need at least two entries per pixel to perform compositing if(pixel->GetSize()>=2) { current=pixel->GetFirst(); next=current->GetNext(); #ifdef BACK_TO_FRONT done=current->GetZview()<=zTarget || next->GetZview()<=zTarget; #else done=current->GetZview()>=zTarget || next->GetZview()>=zTarget; #endif if(!done && this->ZBuffer!=0) { // value of the z buffer at the current pixel. zBuffer=this->GetZBufferValue(x,y); } while(!done) { if(this->ZBuffer!=0) { // check that current and next are in front of the z-buffer value doIntegration=current->GetZview()GetZview()GetZview()!=next->GetZview()) { // length in world coordinates length=sqrt(vtkMath::Distance2BetweenPoints( current->GetValues(),next->GetValues())); if(length!=0) // if(length>=0.4) { color=this->RealRGBAImage+index2; this->IntersectionLengths->SetValue(0,length); this->NearIntersections->SetValue(0,current->GetValues()[VTK_VALUES_SCALAR_INDEX]); this->FarIntersections->SetValue(0,next->GetValues()[VTK_VALUES_SCALAR_INDEX]); #ifdef BACK_TO_FRONT this->RealRayIntegrator->Integrate(this->IntersectionLengths, this->FarIntersections, this->NearIntersections, color); #else this->RealRayIntegrator->Integrate(this->IntersectionLengths, this->NearIntersections, this->FarIntersections, color); #endif } // length!=0 } // current->GetZview()!=next->GetZview() } // doIntegration // Next entry pixel->RemoveFirst(this->MemoryManager); // remove current done=pixel->GetSize()<2; // empty queue? if(!done) { current=next; next=current->GetNext(); #ifdef BACK_TO_FRONT done=next->GetZview()<=zTarget; #else done=next->GetZview()>=zTarget; #endif } } // while(!done) } if(pixel->GetSize()>=2) { if(xnewXBounds[1]) { newXBounds[1]=x; } } if(ynewYBounds[1]) { newYBounds[1]=y; } } } // next abscissa ++j; index2+=4; ++x; } // next ordinate i=i+this->ImageInUseSize[0]; index+=indexStep; ++y; } // Update the bounding box. Useful for the delayed compositing this->XBounds[0]=newXBounds[0]; this->XBounds[1]=newXBounds[1]; this->YBounds[0]=newYBounds[0]; this->YBounds[1]=newYBounds[1]; this->MaxPixelListSizeReached=0; } //----------------------------------------------------------------------------- // Description: // Convert and clamp a float color component into a unsigned char. unsigned char vtkUnstructuredGridVolumeZSweepMapper::ColorComponentRealToByte( float color) { int val=static_cast(color*255.0); if(val>255) { val=255; } else { if(val<0) { val=0; } } return static_cast(val); } //----------------------------------------------------------------------------- double vtkUnstructuredGridVolumeZSweepMapper::GetZBufferValue(int x, int y) { int xPos, yPos; xPos = static_cast(static_cast(x) * this->ImageSampleDistance); yPos = static_cast(static_cast(y) * this->ImageSampleDistance); xPos = (xPos >= this->ZBufferSize[0])?(this->ZBufferSize[0]-1):(xPos); yPos = (yPos >= this->ZBufferSize[1])?(this->ZBufferSize[1]-1):(yPos); return *(this->ZBuffer + yPos*this->ZBufferSize[0] + xPos); } //----------------------------------------------------------------------------- double vtkUnstructuredGridVolumeZSweepMapper::GetMinimumBoundsDepth( vtkRenderer *ren, vtkVolume *vol ) { double bounds[6]; vol->GetBounds( bounds ); ren->ComputeAspect(); double *aspect = ren->GetAspect(); // Get the view matrix in two steps - there is a one step method in camera // but it turns off stereo so we do not want to use that one vtkCamera *cam = ren->GetActiveCamera(); this->PerspectiveTransform->Identity(); this->PerspectiveTransform->Concatenate( cam->GetPerspectiveTransformMatrix(aspect[0]/aspect[1], 0.0, 1.0 )); this->PerspectiveTransform->Concatenate(cam->GetViewTransformMatrix()); this->PerspectiveMatrix->DeepCopy(this->PerspectiveTransform->GetMatrix()); double minZ = 1.0; for ( int k = 0; k < 2; k++ ) { for ( int j = 0; j < 2; j++ ) { for ( int i = 0; i < 2; i++ ) { double inPoint[4]; inPoint[0] = bounds[ i]; inPoint[1] = bounds[2+j]; inPoint[2] = bounds[4+k]; inPoint[3] = 1.0; double outPoint[4]; this->PerspectiveMatrix->MultiplyPoint( inPoint, outPoint ); double testZ = outPoint[2] / outPoint[3]; minZ = ( testZ < minZ ) ? (testZ) : (minZ); } } } return minZ; }