/*========================================================================= Program: Visualization Toolkit Module: vtkStructuredGridLIC2D.cxx 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 "vtkStructuredGridLIC2D.h" #include "vtkOpenGLHelper.h" #include "vtkFloatArray.h" #include "vtkImageData.h" #include "vtkImageNoiseSource.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkDataTransferHelper.h" #include "vtkOpenGLFramebufferObject.h" #include "vtkLineIntegralConvolution2D.h" #include "vtkStructuredExtent.h" #include "vtkTextureObject.h" #include "vtkObjectFactory.h" #include "vtkOpenGLRenderWindow.h" #include "vtkPointData.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkOpenGLError.h" #include "vtkOpenGLShaderCache.h" #include "vtkShaderProgram.h" #include "vtkPixelExtent.h" #include "vtkPixelTransfer.h" #include "vtkPixelBufferObject.h" #include #include "vtkTextureObjectVS.h" #include "vtkStructuredGridLIC2D_fs.h" #define PRINTEXTENT(ext) \ ext[0] << ", " << ext[1] << ", " << ext[2] << ", " << ext[3] << ", " << ext[4] << ", " << ext[5] vtkStandardNewMacro(vtkStructuredGridLIC2D); //---------------------------------------------------------------------------- vtkStructuredGridLIC2D::vtkStructuredGridLIC2D() { this->Context = nullptr; this->Steps = 1; this->StepSize = 1.0; this->Magnification = 1; this->SetNumberOfInputPorts(2); this->SetNumberOfOutputPorts(2); this->OwnWindow = false; this->FBOSuccess = 0; this->LICSuccess = 0; this->NoiseSource = vtkImageNoiseSource::New(); this->NoiseSource->SetWholeExtent(0, 127, 0, 127, 0, 0); this->NoiseSource->SetMinimum(0.0); this->NoiseSource->SetMaximum(1.0); this->LICProgram = nullptr; } //---------------------------------------------------------------------------- vtkStructuredGridLIC2D::~vtkStructuredGridLIC2D() { this->NoiseSource->Delete(); this->SetContext( nullptr ); } //---------------------------------------------------------------------------- vtkRenderWindow* vtkStructuredGridLIC2D::GetContext() { return this->Context; } //---------------------------------------------------------------------------- int vtkStructuredGridLIC2D::SetContext( vtkRenderWindow * context ) { if ( this->Context && this->OwnWindow ) { this->Context->Delete(); this->Context = nullptr; } this->OwnWindow = false; vtkOpenGLRenderWindow * openGLRenWin = vtkOpenGLRenderWindow::SafeDownCast( context ); this->Context = openGLRenWin; this->Modified(); return 1; } //---------------------------------------------------------------------------- // Description: // Fill the input port information objects for this algorithm. This // is invoked by the first call to GetInputPortInformation for each // port so subclasses can specify what they can handle. // Redefined from the superclass. int vtkStructuredGridLIC2D::FillInputPortInformation ( int port, vtkInformation * info ) { if ( port == 0 ) { info->Set( vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkStructuredGrid" ); info->Set( vtkAlgorithm::INPUT_IS_REPEATABLE(), 0 ); info->Set( vtkAlgorithm::INPUT_IS_OPTIONAL(), 0 ); } else { info->Set( vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData" ); info->Set( vtkAlgorithm::INPUT_IS_REPEATABLE(), 0 ); info->Set( vtkAlgorithm::INPUT_IS_OPTIONAL(), 1 ); } return 1; } // ---------------------------------------------------------------------------- // Description: // Fill the output port information objects for this algorithm. // This is invoked by the first call to GetOutputPortInformation for // each port so subclasses can specify what they can handle. // Redefined from the superclass. int vtkStructuredGridLIC2D::FillOutputPortInformation ( int port, vtkInformation * info ) { if ( port == 0 ) { // input+texcoords info->Set( vtkDataObject::DATA_TYPE_NAME(), "vtkStructuredGrid" ); } else { // LIC texture info->Set( vtkDataObject::DATA_TYPE_NAME(), "vtkImageData" ); } return 1; } //---------------------------------------------------------------------------- // We need to report output extent after taking into consideration the // magnification. int vtkStructuredGridLIC2D::RequestInformation( vtkInformation * vtkNotUsed(request), vtkInformationVector ** inputVector, vtkInformationVector * outputVector ) { int ext[6]; double spacing[3]; vtkInformation * inInfo = inputVector[0]->GetInformationObject( 0 ); vtkInformation * outInfo = outputVector->GetInformationObject( 1 ); inInfo->Get( vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), ext ); spacing[0] = 1.0; spacing[1] = 1.0; spacing[2] = 1.0; for (int axis = 0; axis < 3; axis++) { int wholeMin = ext[axis*2]; int wholeMax = ext[axis*2+1]; int dimension = wholeMax - wholeMin + 1; // Scale the output extent wholeMin = static_cast( ceil( static_cast ( wholeMin * this->Magnification ) ) ); wholeMax = ( dimension != 1 ) ? wholeMin + static_cast ( floor( static_cast ( dimension * this->Magnification ) ) ) -1 : wholeMin; ext[ axis * 2 ] = wholeMin; ext[ axis * 2 + 1 ] = wholeMax; } vtkDebugMacro( << "request info whole ext = " << PRINTEXTENT( ext ) << endl ); outInfo->Set( vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), ext, 6 ); outInfo->Set( vtkDataObject::SPACING(), spacing, 3 ); return 1; } //---------------------------------------------------------------------------- int vtkStructuredGridLIC2D::RequestUpdateExtent ( vtkInformation * vtkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *outputVector) { vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); vtkInformation *outInfo = outputVector->GetInformationObject(1); // Tell the vector field input the extents that we need from it. // The downstream request needs to be downsized based on the Magnification. int ext[6]; outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), ext); vtkDebugMacro( << "request update extent, update ext = " << PRINTEXTENT( ext ) << endl ); for (int axis = 0; axis < 3; axis++) { int wholeMin = ext[axis*2]; int wholeMax = ext[axis*2+1]; int dimension = wholeMax - wholeMin + 1; // Scale the output extent wholeMin = static_cast(ceil(static_cast(wholeMin / this->Magnification))); wholeMax = dimension != 1? wholeMin + static_cast(floor(static_cast(dimension / this->Magnification))) -1: ext[axis*2] = wholeMin; ext[axis*2+1] = wholeMax; } vtkDebugMacro( << "UPDATE_EXTENT: " << ext[0] << ", " << ext[1] << ", " << ext[2] << ", " << ext[3] << ", " << ext[4] << ", " << ext[5] << endl ); inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), ext, 6); vtkDebugMacro( << "request update extent, update ext2 = " << PRINTEXTENT( ext ) << endl ); if(inputVector[1]!=nullptr && inputVector[1]->GetInformationObject(0) != nullptr) // optional input { inInfo = inputVector[1]->GetInformationObject(0); // always request the whole extent inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()), 6); } return 1; } //---------------------------------------------------------------------------- // Stolen from vtkImageAlgorithm. Should be in vtkStructuredGridAlgorithm. void vtkStructuredGridLIC2D::AllocateOutputData(vtkDataObject *output, vtkInformation *outInfo) { // set the extent to be the update extent vtkStructuredGrid *out = vtkStructuredGrid::SafeDownCast(output); if (out) { out->SetExtent(outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT())); } else { vtkImageData *out2 = vtkImageData::SafeDownCast(output); if (out2) { out2->SetExtent(outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT())); } } } //---------------------------------------------------------------------------- // Stolen from vtkImageData. Should be in vtkStructuredGrid. void vtkStructuredGridLIC2D::AllocateScalars(vtkStructuredGrid *sg, vtkInformation *outInfo) { int newType = VTK_DOUBLE; int newNumComp = 1; vtkInformation *scalarInfo = vtkDataObject::GetActiveFieldInformation( outInfo, vtkDataObject::FIELD_ASSOCIATION_POINTS, vtkDataSetAttributes::SCALARS); if (scalarInfo) { newType = scalarInfo->Get( vtkDataObject::FIELD_ARRAY_TYPE() ); if ( scalarInfo->Has(vtkDataObject::FIELD_NUMBER_OF_COMPONENTS()) ) { newNumComp = scalarInfo->Get( vtkDataObject::FIELD_NUMBER_OF_COMPONENTS() ); } } vtkDataArray *scalars; // if the scalar type has not been set then we have a problem if (newType == VTK_VOID) { vtkErrorMacro("Attempt to allocate scalars before scalar type was set!."); return; } const int* extent = sg->GetExtent(); // Use vtkIdType to avoid overflow on large images vtkIdType dims[3]; dims[0] = extent[1] - extent[0] + 1; dims[1] = extent[3] - extent[2] + 1; dims[2] = extent[5] - extent[4] + 1; vtkIdType imageSize = dims[0]*dims[1]*dims[2]; // if we currently have scalars then just adjust the size scalars = sg->GetPointData()->GetScalars(); if (scalars && scalars->GetDataType() == newType && scalars->GetReferenceCount() == 1) { scalars->SetNumberOfComponents(newNumComp); scalars->SetNumberOfTuples(imageSize); // Since the execute method will be modifying the scalars // directly. scalars->Modified(); return; } // allocate the new scalars scalars = vtkDataArray::CreateDataArray(newType); scalars->SetNumberOfComponents(newNumComp); // allocate enough memory scalars->SetNumberOfTuples(imageSize); sg->GetPointData()->SetScalars(scalars); scalars->Delete(); } //---------------------------------------------------------------------------- int vtkStructuredGridLIC2D::RequestData( vtkInformation * vtkNotUsed(request), vtkInformationVector ** inputVector, vtkInformationVector * outputVector ) { // 3 passes: // pass 1: render to compute the transformed vector field for the points. // pass 2: perform LIC with the new vector field. This has to happen in a // different pass than computation of the transformed vector. // pass 3: Render structured slice quads with correct texture correct // tcoords and apply the LIC texture to it. vtkInformation * inInfo = inputVector[0]->GetInformationObject(0); vtkStructuredGrid * input = vtkStructuredGrid::SafeDownCast ( inInfo->Get( vtkDataObject::DATA_OBJECT() ) ); int inputRequestedExtent[6]; inInfo->Get( vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inputRequestedExtent); // Check if the input image is a 2D image (not 0D, not 1D, not 3D) int dims[3]; vtkStructuredExtent::GetDimensions( inputRequestedExtent, dims ); vtkDebugMacro( << "dims = " << dims[0] << " " << dims[1] << " " << dims[2] << endl ); vtkDebugMacro( << "requested ext = " << inputRequestedExtent[0] << " " << inputRequestedExtent[1] << " " << inputRequestedExtent[2] << " " << inputRequestedExtent[3] << " " << inputRequestedExtent[4] << " " << inputRequestedExtent[5] << endl ); if( !( (dims[0] == 1) && (dims[1] > 1) && (dims[2] > 1) ) && !( (dims[1] == 1) && (dims[0] > 1) && (dims[2] > 1) ) && !( (dims[2] == 1) && (dims[0] > 1) && (dims[1] > 1) ) ) { vtkErrorMacro( << "input is not a 2D image." << endl ); input = nullptr; inInfo = nullptr; return 0; } if( input->GetPointData() == nullptr ) { vtkErrorMacro( << "input does not have point data." ); input = nullptr; inInfo = nullptr; return 0; } if( input->GetPointData()->GetVectors() == nullptr ) { vtkErrorMacro( << "input does not vectors on point data." ); input = nullptr; inInfo = nullptr; return 0; } if ( !this->Context ) { vtkRenderWindow * renWin = vtkRenderWindow::New(); if ( this->SetContext( renWin ) == 0 ) { vtkErrorMacro("Invalid render window"); renWin->Delete(); renWin = nullptr; input = nullptr; inInfo = nullptr; return 0; } renWin = nullptr; // to be released via this->context this->OwnWindow = true; } vtkInformation * outInfo = outputVector->GetInformationObject(0); vtkStructuredGrid * output = vtkStructuredGrid::SafeDownCast( outInfo->Get(vtkDataObject::DATA_OBJECT())); this->AllocateOutputData(output, outInfo); output->ShallowCopy(input); vtkInformation * outInfoTexture = outputVector->GetInformationObject(1); vtkImageData * outputTexture = vtkImageData::SafeDownCast( outInfoTexture->Get(vtkDataObject::DATA_OBJECT())); this->AllocateOutputData(outputTexture, outInfoTexture); // Noise. vtkInformation *noiseInfo = inputVector[1]->GetInformationObject(0); vtkImageData *noise = nullptr; if (noiseInfo == nullptr) { this->NoiseSource->Update(); noise = this->NoiseSource->GetOutput(); } else { noise = vtkImageData::SafeDownCast( noiseInfo->Get(vtkDataObject::DATA_OBJECT())); if(noise->GetPointData()==nullptr) { vtkErrorMacro(<<"provided noise does not have point data."); return 0; } if(noise->GetPointData()->GetScalars()==nullptr) { vtkErrorMacro(<<"provided noise does not have scalars on point data."); return 0; } } vtkOpenGLClearErrorMacro(); int width; int height; int firstComponent; int secondComponent; int slice; if (dims[0] == 1) { vtkDebugMacro( << "x" << endl ); firstComponent = 1; secondComponent = 2; slice = 0; } else { if (dims[1] == 1) { vtkDebugMacro( << "y" << endl ); firstComponent = 0; secondComponent = 2; slice = 1; } else { vtkDebugMacro( << "z" << endl ); firstComponent = 0; secondComponent = 1; slice = 2; } } width = dims[firstComponent]; height = dims[secondComponent]; vtkDebugMacro( << "w = " << width << " h = " << height << endl ); vtkDataTransferHelper * vectorFieldBus = vtkDataTransferHelper::New(); vectorFieldBus->SetContext(this->Context); vectorFieldBus->SetCPUExtent(inputRequestedExtent); // input->GetExtent()); vectorFieldBus->SetGPUExtent(inputRequestedExtent); // input->GetExtent()); // vectorFieldBus->SetTextureExtent(input->GetExtent()); vectorFieldBus->SetArray(input->GetPointData()->GetVectors()); vtkDataTransferHelper * pointBus = vtkDataTransferHelper::New(); pointBus->SetContext(this->Context); pointBus->SetCPUExtent(inputRequestedExtent); // input->GetExtent()); pointBus->SetGPUExtent(inputRequestedExtent); // input->GetExtent()); // pointBus->SetTextureExtent(input->GetExtent()); pointBus->SetArray(input->GetPoints()->GetData()); // Vector field in image space. int magWidth = this->Magnification*width; int magHeight = this->Magnification*height; vtkOpenGLRenderWindow *renWin = vtkOpenGLRenderWindow::SafeDownCast(this->Context); vtkTextureObject * vector2 = vtkTextureObject::New(); vector2->SetContext(renWin); vector2->Create2D(magWidth,magHeight,3,VTK_FLOAT,false); vtkDebugMacro( << "Vector field in image space (target) textureId = " << vector2->GetHandle() << endl ); vtkOpenGLFramebufferObject *fbo = vtkOpenGLFramebufferObject::New(); fbo->SetContext(renWin); fbo->Bind(); fbo->AddColorAttachment(fbo->GetBothMode(), 0,vector2); fbo->ActivateDrawBuffer(0); fbo->ActivateReadBuffer(0); // TODO -- // step size is incorrect here // guard pixels are needed for parallel operations if ( !fbo->Start( magWidth, magHeight ) ) { fbo->Delete(); vector2->Delete(); pointBus->Delete(); vectorFieldBus->Delete(); fbo = nullptr; vector2 = nullptr; pointBus = nullptr; vectorFieldBus = nullptr; noise = nullptr; input = nullptr; inInfo = nullptr; output = nullptr; outInfo = nullptr; noiseInfo = nullptr; outputTexture = nullptr; outInfoTexture = nullptr; this->FBOSuccess = 0; return 0; } this->FBOSuccess = 1; // build the shader program this->LICProgram = new vtkOpenGLHelper; std::string VSSource = vtkTextureObjectVS; std::string FSSource = vtkStructuredGridLIC2D_fs; std::string GSSource; this->LICProgram->Program = renWin->GetShaderCache()->ReadyShaderProgram( VSSource.c_str(), FSSource.c_str(), GSSource.c_str()); vtkShaderProgram *pgm = this->LICProgram->Program; float fvalues[3]; fvalues[0] = static_cast(dims[0]); fvalues[1] = static_cast(dims[1]); fvalues[2] = static_cast(dims[2]); pgm->SetUniform3f("uDimensions",fvalues); pgm->SetUniformi("uSlice",slice); pointBus->Upload(0,nullptr); vtkTextureObject *points = pointBus->GetTexture(); points->SetWrapS(vtkTextureObject::ClampToEdge); points->SetWrapT(vtkTextureObject::ClampToEdge); points->SetWrapR(vtkTextureObject::ClampToEdge); vectorFieldBus->Upload(0,nullptr); vtkTextureObject *vectorField = vectorFieldBus->GetTexture(); vectorField->SetWrapS(vtkTextureObject::ClampToEdge); vectorField->SetWrapT(vtkTextureObject::ClampToEdge); vectorField->SetWrapR(vtkTextureObject::ClampToEdge); points->Activate(); pgm->SetUniformi("texPoints",points->GetTextureUnit()); vectorField->Activate(); pgm->SetUniformi("texVectorField",vectorField->GetTextureUnit()); vtkOpenGLCheckErrorMacro("failed during config"); vtkDebugMacro( << "glFinish before rendering quad" << endl ); fbo->RenderQuad(0, magWidth-1,0, magHeight-1, pgm, this->LICProgram->VAO); vtkOpenGLCheckErrorMacro("StructuredGridLIC2D projection fialed"); vtkDebugMacro( << "glFinish after rendering quad" << endl ); vtkLineIntegralConvolution2D *internal = vtkLineIntegralConvolution2D::New(); if ( !internal->IsSupported( this->Context ) ) { this->LICProgram->ReleaseGraphicsResources(renWin); delete this->LICProgram; this->LICProgram = nullptr; fbo->Delete(); vector2->Delete(); internal->Delete(); pointBus->Delete(); vectorFieldBus->Delete(); fbo = nullptr; vector2 = nullptr; internal = nullptr; pointBus = nullptr; vectorFieldBus = nullptr; noise = nullptr; input = nullptr; inInfo = nullptr; points = nullptr; output = nullptr; outInfo = nullptr; noiseInfo = nullptr; vectorField = nullptr; outputTexture = nullptr; outInfoTexture = nullptr; this->LICSuccess = 0; return 0; } internal->SetContext(renWin); internal->SetNumberOfSteps(this->Steps); internal->SetStepSize(this->StepSize); internal->SetComponentIds(firstComponent,secondComponent); vtkDataTransferHelper *noiseBus = vtkDataTransferHelper::New(); noiseBus->SetContext(this->Context); noiseBus->SetCPUExtent(noise->GetExtent()); noiseBus->SetGPUExtent(noise->GetExtent()); // noiseBus->SetTextureExtent(noise->GetExtent()); noiseBus->SetArray(noise->GetPointData()->GetScalars()); noiseBus->Upload(0,nullptr); vtkTextureObject *licTex = internal->Execute(vector2, noiseBus->GetTexture()); if (licTex == nullptr) { this->LICProgram->ReleaseGraphicsResources(renWin); delete this->LICProgram; this->LICProgram = nullptr; fbo->Delete(); vector2->Delete(); internal->Delete(); pointBus->Delete(); noiseBus->Delete(); vectorFieldBus->Delete(); fbo = nullptr; vector2 = nullptr; internal = nullptr; pointBus = nullptr; noiseBus = nullptr; vectorFieldBus = nullptr; noise = nullptr; input = nullptr; inInfo = nullptr; points = nullptr; output = nullptr; outInfo = nullptr; noiseInfo = nullptr; vectorField = nullptr; outputTexture = nullptr; outInfoTexture = nullptr; this->LICSuccess = 0; return 0; } this->LICSuccess = 1; // transfer lic from texture to vtk array vtkPixelExtent magLicExtent(magWidth, magHeight); vtkIdType nOutTups = magLicExtent.Size(); vtkFloatArray *licOut = vtkFloatArray::New(); licOut->SetNumberOfComponents(3); licOut->SetNumberOfTuples(nOutTups); licOut->SetName("LIC"); vtkPixelBufferObject *licPBO = licTex->Download(); vtkPixelTransfer::Blit( magLicExtent, magLicExtent, magLicExtent, magLicExtent, 4, (float*)licPBO->MapPackedBuffer(), 3, licOut->GetPointer(0)); licPBO->UnmapPackedBuffer(); licPBO->Delete(); licTex->Delete(); // mask and convert to gray scale 3 components float *pLicOut = licOut->GetPointer(0); for (vtkIdType i=0; iGetPointData()->SetScalars(licOut); licOut->Delete(); // Pass three. Generate texture coordinates. Software. vtkFloatArray *tcoords = vtkFloatArray::New(); tcoords->SetNumberOfComponents(2); tcoords->SetNumberOfTuples(dims[0]*dims[1]*dims[2]); output->GetPointData()->SetTCoords(tcoords); tcoords->Delete(); double ddim[3]; ddim[0] = static_cast(dims[0]-1); ddim[1] = static_cast(dims[1]-1); ddim[2] = static_cast(dims[2]-1); int tz = 0; while(tz < dims[slice]) { int ty = 0; while(ty < dims[secondComponent]) { int tx = 0; while(tx < dims[firstComponent]) { tcoords->SetTuple2( (tz*dims[secondComponent]+ty)*dims[firstComponent]+tx, tx/ddim[firstComponent], ty/ddim[secondComponent]); ++tx; } ++ty; } ++tz; } internal->Delete(); noiseBus->Delete(); vectorFieldBus->Delete(); pointBus->Delete(); vector2->Delete(); fbo->Delete(); this->LICProgram->ReleaseGraphicsResources(renWin); delete this->LICProgram; this->LICProgram = nullptr; vtkOpenGLCheckErrorMacro("failed after RequestData"); return 1; } //---------------------------------------------------------------------------- void vtkStructuredGridLIC2D::PrintSelf( ostream & os, vtkIndent indent ) { this->Superclass::PrintSelf( os, indent ); os << indent << "Steps: " << this->Steps << "\n"; os << indent << "StepSize: " << this->StepSize << "\n"; os << indent << "FBOSuccess: " << this->FBOSuccess << "\n"; os << indent << "LICSuccess: " << this->LICSuccess << "\n"; os << indent << "Magnification: " << this->Magnification << "\n"; }