/*========================================================================= Program: Visualization Toolkit 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 "vtkOpenGLPointGaussianMapper.h" #include "vtkOpenGLHelper.h" #include "vtkBoundingBox.h" #include "vtkCellArray.h" #include "vtkCommand.h" #include "vtkCompositeDataIterator.h" #include "vtkCompositeDataPipeline.h" #include "vtkCompositeDataSet.h" #include "vtkDataObjectTreeIterator.h" #include "vtkFloatArray.h" #include "vtkGarbageCollector.h" #include "vtkHardwareSelector.h" #include "vtkInformation.h" #include "vtkMath.h" #include "vtkMatrix4x4.h" #include "vtkObjectFactory.h" #include "vtkOpenGLActor.h" #include "vtkOpenGLCamera.h" #include "vtkOpenGLIndexBufferObject.h" #include "vtkOpenGLPolyDataMapper.h" #include "vtkOpenGLRenderWindow.h" #include "vtkOpenGLRenderer.h" #include "vtkOpenGLState.h" #include "vtkOpenGLVertexArrayObject.h" #include "vtkOpenGLVertexBufferObject.h" #include "vtkOpenGLVertexBufferObjectGroup.h" #include "vtkPiecewiseFunction.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkProperty.h" #include "vtkShaderProgram.h" #include "vtkUnsignedCharArray.h" #include "vtkPointGaussianGS.h" #include "vtkPointGaussianVS.h" #include "vtkPolyDataFS.h" #include "vtk_glew.h" class vtkOpenGLPointGaussianMapperHelper : public vtkOpenGLPolyDataMapper { public: static vtkOpenGLPointGaussianMapperHelper* New(); vtkTypeMacro(vtkOpenGLPointGaussianMapperHelper, vtkOpenGLPolyDataMapper); vtkPointGaussianMapper* Owner; // set from parent float* OpacityTable; // the table double OpacityScale; // used for quick lookups double OpacityOffset; // used for quick lookups float* ScaleTable; // the table double ScaleScale; // used for quick lookups double ScaleOffset; // used for quick lookups vtkIdType FlatIndex; bool UsingPoints; double TriangleScale; // called by our Owner skips some stuff void GaussianRender(vtkRenderer* ren, vtkActor* act); protected: vtkOpenGLPointGaussianMapperHelper(); ~vtkOpenGLPointGaussianMapperHelper() override; // Description: // Create the basic shaders before replacement void GetShaderTemplate( std::map shaders, vtkRenderer*, vtkActor*) override; // Description: // Perform string replacements on the shader templates void ReplaceShaderColor( std::map shaders, vtkRenderer*, vtkActor*) override; void ReplaceShaderPositionVC( std::map shaders, vtkRenderer*, vtkActor*) override; // Description: // Set the shader parameters related to the Camera void SetCameraShaderParameters(vtkOpenGLHelper& cellBO, vtkRenderer* ren, vtkActor* act) override; // Description: // Set the shader parameters related to the actor/mapper void SetMapperShaderParameters(vtkOpenGLHelper& cellBO, vtkRenderer* ren, vtkActor* act) override; // Description: // Does the VBO/IBO need to be rebuilt bool GetNeedToRebuildBufferObjects(vtkRenderer* ren, vtkActor* act) override; // Description: // Update the VBO to contain point based values void BuildBufferObjects(vtkRenderer* ren, vtkActor* act) override; void RenderPieceDraw(vtkRenderer* ren, vtkActor* act) override; // Description: // Does the shader source need to be recomputed bool GetNeedToRebuildShaders(vtkOpenGLHelper& cellBO, vtkRenderer* ren, vtkActor* act) override; private: vtkOpenGLPointGaussianMapperHelper(const vtkOpenGLPointGaussianMapperHelper&) = delete; void operator=(const vtkOpenGLPointGaussianMapperHelper&) = delete; }; //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkOpenGLPointGaussianMapperHelper); //------------------------------------------------------------------------------ vtkOpenGLPointGaussianMapperHelper::vtkOpenGLPointGaussianMapperHelper() { this->Owner = nullptr; this->UsingPoints = false; this->TriangleScale = 0.0; this->FlatIndex = 1; this->OpacityTable = nullptr; this->ScaleTable = nullptr; this->OpacityScale = 1.0; this->ScaleScale = 1.0; this->OpacityOffset = 0.0; this->ScaleOffset = 0.0; } //------------------------------------------------------------------------------ void vtkOpenGLPointGaussianMapperHelper::GetShaderTemplate( std::map shaders, vtkRenderer* ren, vtkActor* actor) { this->Superclass::GetShaderTemplate(shaders, ren, actor); if (this->Owner->GetScaleFactor() == 0.0) { this->UsingPoints = true; } else { this->UsingPoints = false; // for splats use a special shader that handles the offsets shaders[vtkShader::Vertex]->SetSource(vtkPointGaussianVS); shaders[vtkShader::Geometry]->SetSource(vtkPointGaussianGS); } } void vtkOpenGLPointGaussianMapperHelper::ReplaceShaderPositionVC( std::map shaders, vtkRenderer* ren, vtkActor* actor) { if (!this->UsingPoints) { std::string VSSource = shaders[vtkShader::Vertex]->GetSource(); std::string FSSource = shaders[vtkShader::Fragment]->GetSource(); vtkShaderProgram::Substitute(FSSource, "//VTK::PositionVC::Dec", "in vec2 offsetVCVSOutput;"); vtkShaderProgram::Substitute(VSSource, "//VTK::Camera::Dec", "uniform mat4 VCDCMatrix;\n" "uniform mat4 MCVCMatrix;"); shaders[vtkShader::Vertex]->SetSource(VSSource); shaders[vtkShader::Fragment]->SetSource(FSSource); } this->Superclass::ReplaceShaderPositionVC(shaders, ren, actor); } void vtkOpenGLPointGaussianMapperHelper::ReplaceShaderColor( std::map shaders, vtkRenderer* ren, vtkActor* actor) { if (!this->UsingPoints) { std::string FSSource = shaders[vtkShader::Fragment]->GetSource(); if (this->Owner->GetSplatShaderCode() && strcmp(this->Owner->GetSplatShaderCode(), "") != 0) { vtkShaderProgram::Substitute( FSSource, "//VTK::Color::Impl", this->Owner->GetSplatShaderCode(), false); } else { vtkShaderProgram::Substitute(FSSource, "//VTK::Color::Impl", // compute the eye position and unit direction "//VTK::Color::Impl\n" " float dist2 = dot(offsetVCVSOutput.xy,offsetVCVSOutput.xy);\n" " float gaussian = exp(-0.5*dist2);\n" " opacity = opacity*gaussian;" // " opacity = opacity*0.5;" , false); } shaders[vtkShader::Fragment]->SetSource(FSSource); } this->Superclass::ReplaceShaderColor(shaders, ren, actor); // cerr << shaders[vtkShader::Fragment]->GetSource() << endl; } //------------------------------------------------------------------------------ bool vtkOpenGLPointGaussianMapperHelper::GetNeedToRebuildShaders( vtkOpenGLHelper& cellBO, vtkRenderer* ren, vtkActor* actor) { this->PrimitiveInfo[&cellBO].LastLightComplexity = 0; vtkHardwareSelector* selector = ren->GetSelector(); int picking = selector ? selector->GetCurrentPass() : -1; if (this->LastSelectionState != picking) { this->SelectionStateChanged.Modified(); this->LastSelectionState = picking; } vtkMTimeType renderPassMTime = this->GetRenderPassStageMTime(actor, &cellBO); // has something changed that would require us to recreate the shader? // candidates are // property modified (representation interpolation and lighting) // input modified // light complexity changed if (cellBO.Program == nullptr || cellBO.ShaderSourceTime < this->GetMTime() || cellBO.ShaderSourceTime < actor->GetMTime() || cellBO.ShaderSourceTime < this->CurrentInput->GetMTime() || cellBO.ShaderSourceTime < this->SelectionStateChanged || cellBO.ShaderSourceTime < renderPassMTime) { return true; } return false; } //------------------------------------------------------------------------------ vtkOpenGLPointGaussianMapperHelper::~vtkOpenGLPointGaussianMapperHelper() = default; //------------------------------------------------------------------------------ void vtkOpenGLPointGaussianMapperHelper::SetCameraShaderParameters( vtkOpenGLHelper& cellBO, vtkRenderer* ren, vtkActor* actor) { if (this->UsingPoints) { this->Superclass::SetCameraShaderParameters(cellBO, ren, actor); } else { vtkShaderProgram* program = cellBO.Program; vtkOpenGLCamera* cam = (vtkOpenGLCamera*)(ren->GetActiveCamera()); vtkMatrix4x4* wcdc; vtkMatrix4x4* wcvc; vtkMatrix3x3* norms; vtkMatrix4x4* vcdc; cam->GetKeyMatrices(ren, wcvc, norms, vcdc, wcdc); program->SetUniformMatrix("VCDCMatrix", vcdc); if (!actor->GetIsIdentity()) { vtkMatrix4x4* mcwc; vtkMatrix3x3* anorms; ((vtkOpenGLActor*)actor)->GetKeyMatrices(mcwc, anorms); vtkMatrix4x4::Multiply4x4(mcwc, wcvc, this->TempMatrix4); program->SetUniformMatrix("MCVCMatrix", this->TempMatrix4); } else { program->SetUniformMatrix("MCVCMatrix", wcvc); } // add in uniforms for parallel and distance cellBO.Program->SetUniformi("cameraParallel", cam->GetParallelProjection()); } } //------------------------------------------------------------------------------ void vtkOpenGLPointGaussianMapperHelper::SetMapperShaderParameters( vtkOpenGLHelper& cellBO, vtkRenderer* ren, vtkActor* actor) { if (!this->UsingPoints) { cellBO.Program->SetUniformf("triangleScale", this->TriangleScale); } this->Superclass::SetMapperShaderParameters(cellBO, ren, actor); } namespace { template PointDataType vtkOpenGLPointGaussianMapperHelperGetComponent( PointDataType* tuple, int nComponent, int component) { // If this is a single component array, make sure we do not compute // a useless magnitude if (nComponent == 1) { component = 0; } // If we request a non-existing component, return the magnitude of the tuple PointDataType compVal = 0.0; if (component < 0 || component >= nComponent) { for (int iComp = 0; iComp < nComponent; iComp++) { PointDataType tmp = tuple[iComp]; compVal += tmp * tmp; } compVal = sqrt(compVal); } else { compVal = tuple[component]; } return compVal; } void vtkOpenGLPointGaussianMapperHelperComputeColor(unsigned char* rcolor, unsigned char* colors, int colorComponents, vtkIdType index, vtkDataArray* opacities, int opacitiesComponent, vtkOpenGLPointGaussianMapperHelper* self) { unsigned char white[4] = { 255, 255, 255, 255 }; // if there are no per point sizes and the default size is zero // then just render points, saving memory and speed unsigned char* colorPtr = colors ? (colors + index * colorComponents) : white; rcolor[0] = *(colorPtr++); rcolor[1] = *(colorPtr++); rcolor[2] = *(colorPtr++); if (opacities) { double opacity = vtkOpenGLPointGaussianMapperHelperGetComponent( opacities->GetTuple(index), opacities->GetNumberOfComponents(), opacitiesComponent); if (self->OpacityTable) { double tindex = (opacity - self->OpacityOffset) * self->OpacityScale; int itindex = static_cast(tindex); if (itindex >= self->Owner->GetOpacityTableSize() - 1) { opacity = self->OpacityTable[self->Owner->GetOpacityTableSize() - 1]; } else if (itindex < 0) { opacity = self->OpacityTable[0]; } else { opacity = (1.0 - tindex + itindex) * self->OpacityTable[itindex] + (tindex - itindex) * self->OpacityTable[itindex + 1]; } } rcolor[3] = static_cast(opacity * 255.0); } else { rcolor[3] = (colorComponents == 4 ? *colorPtr : 255); } } void vtkOpenGLPointGaussianMapperHelperColors(vtkUnsignedCharArray* outColors, vtkIdType numPts, unsigned char* colors, int colorComponents, vtkDataArray* opacities, int opacitiesComponent, vtkOpenGLPointGaussianMapperHelper* self, vtkCellArray* verts) { unsigned char* vPtr = static_cast(outColors->GetVoidPointer(0)); // iterate over cells or not if (verts->GetNumberOfCells()) { const vtkIdType* indices(nullptr); vtkIdType npts(0); for (verts->InitTraversal(); verts->GetNextCell(npts, indices);) { for (int i = 0; i < npts; ++i) { vtkOpenGLPointGaussianMapperHelperComputeColor( vPtr, colors, colorComponents, indices[i], opacities, opacitiesComponent, self); vPtr += 4; } } } else { for (vtkIdType i = 0; i < numPts; i++) { vtkOpenGLPointGaussianMapperHelperComputeColor( vPtr, colors, colorComponents, i, opacities, opacitiesComponent, self); vPtr += 4; } } } float vtkOpenGLPointGaussianMapperHelperGetRadius( double radius, vtkOpenGLPointGaussianMapperHelper* self) { if (self->ScaleTable) { double tindex = (radius - self->ScaleOffset) * self->ScaleScale; int itindex = static_cast(tindex); if (itindex >= self->Owner->GetScaleTableSize() - 1) { radius = self->ScaleTable[self->Owner->GetScaleTableSize() - 1]; } else if (itindex < 0) { radius = self->ScaleTable[0]; } else { radius = (1.0 - tindex + itindex) * self->ScaleTable[itindex] + (tindex - itindex) * self->ScaleTable[itindex + 1]; } } radius *= self->Owner->GetScaleFactor(); radius *= self->TriangleScale; return static_cast(radius); } template void vtkOpenGLPointGaussianMapperHelperSizes(vtkFloatArray* scales, PointDataType* sizes, int nComponent, int component, vtkIdType numPts, vtkOpenGLPointGaussianMapperHelper* self, vtkCellArray* verts) { float* it = static_cast(scales->GetVoidPointer(0)); // iterate over cells or not if (verts->GetNumberOfCells()) { const vtkIdType* indices(nullptr); vtkIdType npts(0); for (verts->InitTraversal(); verts->GetNextCell(npts, indices);) { for (vtkIdType i = 0; i < npts; ++i) { PointDataType size = 1.0; if (sizes) { size = vtkOpenGLPointGaussianMapperHelperGetComponent( &sizes[indices[i] * nComponent], nComponent, component); } float radiusFloat = vtkOpenGLPointGaussianMapperHelperGetRadius(size, self); *(it++) = radiusFloat; } } } else { for (vtkIdType i = 0; i < numPts; i++) { PointDataType size = 1.0; if (sizes) { size = vtkOpenGLPointGaussianMapperHelperGetComponent( &sizes[i * nComponent], nComponent, component); } float radiusFloat = vtkOpenGLPointGaussianMapperHelperGetRadius(size, self); *(it++) = radiusFloat; } } } } // anonymous namespace //------------------------------------------------------------------------------ bool vtkOpenGLPointGaussianMapperHelper::GetNeedToRebuildBufferObjects( vtkRenderer* vtkNotUsed(ren), vtkActor* act) { if (this->VBOBuildTime < this->GetMTime() || this->VBOBuildTime < act->GetMTime() || this->VBOBuildTime < this->CurrentInput->GetMTime() || this->VBOBuildTime < this->Owner->GetMTime() || (this->Owner->GetScalarOpacityFunction() && this->VBOBuildTime < this->Owner->GetScalarOpacityFunction()->GetMTime()) || (this->Owner->GetScaleFunction() && this->VBOBuildTime < this->Owner->GetScaleFunction()->GetMTime())) { return true; } return false; } //------------------------------------------------------------------------------ void vtkOpenGLPointGaussianMapperHelper::BuildBufferObjects( vtkRenderer* ren, vtkActor* vtkNotUsed(act)) { vtkPolyData* poly = this->CurrentInput; if (poly == nullptr) { return; } // set the triangle scale this->TriangleScale = this->Owner->GetTriangleScale(); bool hasScaleArray = this->Owner->GetScaleArray() != nullptr && poly->GetPointData()->HasArray(this->Owner->GetScaleArray()); this->UsingPoints = this->Owner->GetScaleFactor() == 0.0; // if we have an opacity array then get it and if we have // a ScalarOpacityFunction map the array through it bool hasOpacityArray = this->Owner->GetOpacityArray() != nullptr && poly->GetPointData()->HasArray(this->Owner->GetOpacityArray()); // For vertex coloring, this sets this->Colors as side effect. // For texture map coloring, this sets ColorCoordinates // and ColorTextureMap as a side effect. // I moved this out of the conditional because it is fast. // Color arrays are cached. If nothing has changed, // then the scalars do not have to be regenerted. this->MapScalars(1.0); int splatCount = poly->GetPoints()->GetNumberOfPoints(); if (poly->GetVerts()->GetNumberOfCells()) { splatCount = poly->GetVerts()->GetNumberOfConnectivityIds(); } // need to build points? if (poly->GetVerts()->GetNumberOfCells()) { vtkFloatArray* pts = vtkFloatArray::New(); pts->SetNumberOfComponents(3); pts->SetNumberOfTuples(splatCount); auto srcData = poly->GetPoints()->GetData(); const auto srcTuples = vtk::DataArrayTupleRange<3>(srcData); auto dstTuples = vtk::DataArrayTupleRange<3>(pts); auto dstIter = dstTuples.begin(); auto verts = poly->GetVerts(); const vtkIdType* indices(nullptr); vtkIdType npts(0); for (verts->InitTraversal(); verts->GetNextCell(npts, indices);) { for (vtkIdType i = 0; i < npts; ++i) { auto srcIter = srcTuples[indices[i]]; auto dst = *dstIter; dst[0] = srcIter[0]; dst[1] = srcIter[1]; dst[2] = srcIter[2]; ++dstIter; } } this->VBOs->CacheDataArray("vertexMC", pts, ren, VTK_FLOAT); pts->Delete(); } else // just pass the points { this->VBOs->CacheDataArray("vertexMC", poly->GetPoints()->GetData(), ren, VTK_FLOAT); } if (!this->UsingPoints) { vtkFloatArray* offsets = vtkFloatArray::New(); offsets->SetNumberOfComponents(1); offsets->SetNumberOfTuples(splatCount); if (hasScaleArray) { vtkDataArray* sizes = poly->GetPointData()->GetArray(this->Owner->GetScaleArray()); switch (sizes->GetDataType()) { vtkTemplateMacro(vtkOpenGLPointGaussianMapperHelperSizes(offsets, static_cast(sizes->GetVoidPointer(0)), sizes->GetNumberOfComponents(), this->Owner->GetScaleArrayComponent(), poly->GetPoints()->GetNumberOfPoints(), this, poly->GetVerts())); } } else { vtkOpenGLPointGaussianMapperHelperSizes(offsets, static_cast(nullptr), 0, 0, poly->GetPoints()->GetNumberOfPoints(), this, poly->GetVerts()); } this->VBOs->CacheDataArray("radiusMC", offsets, ren, VTK_FLOAT); offsets->Delete(); } else { this->VBOs->CacheDataArray("radiusMC", nullptr, ren, VTK_FLOAT); } if (this->Colors) { vtkUnsignedCharArray* clrs = vtkUnsignedCharArray::New(); clrs->SetNumberOfComponents(4); clrs->SetNumberOfTuples(splatCount); vtkOpenGLPointGaussianMapperHelperColors(clrs, poly->GetPoints()->GetNumberOfPoints(), this->Colors ? (unsigned char*)this->Colors->GetVoidPointer(0) : (unsigned char*)nullptr, this->Colors ? this->Colors->GetNumberOfComponents() : 0, hasOpacityArray ? poly->GetPointData()->GetArray(this->Owner->GetOpacityArray()) : (vtkDataArray*)nullptr, this->Owner->GetOpacityArrayComponent(), this, poly->GetVerts()); this->VBOs->CacheDataArray("scalarColor", clrs, ren, VTK_UNSIGNED_CHAR); clrs->Delete(); } this->VBOs->BuildAllVBOs(ren); // we use no IBO for (int i = PrimitiveStart; i < PrimitiveEnd; i++) { this->Primitives[i].IBO->IndexCount = 0; } this->Primitives[PrimitiveTris].IBO->IndexCount = splatCount; this->VBOBuildTime.Modified(); } //------------------------------------------------------------------------------ void vtkOpenGLPointGaussianMapperHelper::RenderPieceDraw(vtkRenderer* ren, vtkActor* actor) { // draw polygons int numVerts = this->VBOs->GetNumberOfTuples("vertexMC"); if (numVerts) { this->UpdateShaders(this->Primitives[PrimitiveTris], ren, actor); glDrawArrays(GL_POINTS, 0, static_cast(numVerts)); } } namespace { // helper to get the state of picking int getPickState(vtkRenderer* ren) { vtkHardwareSelector* selector = ren->GetSelector(); if (selector) { return selector->GetCurrentPass(); } return vtkHardwareSelector::MIN_KNOWN_PASS - 1; } } //------------------------------------------------------------------------------ void vtkOpenGLPointGaussianMapperHelper::GaussianRender(vtkRenderer* ren, vtkActor* actor) { int picking = getPickState(ren); if (this->LastSelectionState != picking) { this->SelectionStateChanged.Modified(); this->LastSelectionState = picking; } this->LastBoundBO = nullptr; this->CurrentInput = this->GetInput(); this->UpdateBufferObjects(ren, actor); this->RenderPieceDraw(ren, actor); if (this->LastBoundBO) { this->LastBoundBO->VAO->Release(); } } //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkOpenGLPointGaussianMapper); //------------------------------------------------------------------------------ vtkOpenGLPointGaussianMapper::vtkOpenGLPointGaussianMapper() { this->OpacityTable = nullptr; this->ScaleTable = nullptr; this->OpacityScale = 1.0; this->ScaleScale = 1.0; this->OpacityOffset = 0.0; this->ScaleOffset = 0.0; } vtkOpenGLPointGaussianMapper::~vtkOpenGLPointGaussianMapper() { if (this->OpacityTable) { delete[] this->OpacityTable; this->OpacityTable = nullptr; } if (this->ScaleTable) { delete[] this->ScaleTable; this->ScaleTable = nullptr; } // clear old helpers carefully due to garbage collection loops for (auto hiter = this->Helpers.begin(); hiter != this->Helpers.end(); ++hiter) { // these pointers may be set to nullptr by the garbage collector // since we are passing them in using ReportReferences if (*hiter) { (*hiter)->Delete(); } } this->Helpers.clear(); } void vtkOpenGLPointGaussianMapper::ReportReferences(vtkGarbageCollector* collector) { // Report references held by this object that may be in a loop. this->Superclass::ReportReferences(collector); // helpers is a vector for (auto hiter = this->Helpers.begin(); hiter != this->Helpers.end(); ++hiter) { vtkGarbageCollectorReport(collector, *hiter, "vtkOpenGLPointGaussianMapperHelper"); } } void vtkOpenGLPointGaussianMapper::Render(vtkRenderer* ren, vtkActor* actor) { // Make sure that we have been properly initialized. if (ren->GetRenderWindow()->CheckAbortStatus()) { return; } if (this->GetInputAlgorithm() == nullptr) { return; } if (!this->Static) { this->InvokeEvent(vtkCommand::StartEvent, nullptr); this->GetInputAlgorithm()->Update(); this->InvokeEvent(vtkCommand::EndEvent, nullptr); } if (this->GetInputDataObject(0, 0) == nullptr) { vtkErrorMacro(<< "No input!"); return; } // update tables if (this->GetScaleFunction() && this->GetScaleArray()) { if (this->ScaleTableUpdateTime < this->GetScaleFunction()->GetMTime() || this->ScaleTableUpdateTime < this->GetMTime()) { this->BuildScaleTable(); this->ScaleTableUpdateTime.Modified(); } } else { delete[] this->ScaleTable; this->ScaleTable = nullptr; } if (this->GetScalarOpacityFunction() && this->GetOpacityArray()) { if (this->OpacityTableUpdateTime < this->GetScalarOpacityFunction()->GetMTime() || this->OpacityTableUpdateTime < this->GetMTime()) { this->BuildOpacityTable(); this->OpacityTableUpdateTime.Modified(); } } else { delete[] this->OpacityTable; this->OpacityTable = nullptr; } // the first step is to update the helpers if needed if (this->HelperUpdateTime < this->GetInputDataObject(0, 0)->GetMTime() || this->HelperUpdateTime < this->GetInputAlgorithm()->GetMTime() || this->HelperUpdateTime < this->GetMTime()) { // clear old helpers for (auto hiter = this->Helpers.begin(); hiter != this->Helpers.end(); ++hiter) { (*hiter)->Delete(); } this->Helpers.clear(); // build new helpers vtkCompositeDataSet* input = vtkCompositeDataSet::SafeDownCast(this->GetInputDataObject(0, 0)); if (input) { vtkSmartPointer iter = vtkSmartPointer::New(); iter->SetDataSet(input); iter->SkipEmptyNodesOn(); iter->VisitOnlyLeavesOn(); for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem()) { unsigned int flatIndex = iter->GetCurrentFlatIndex(); vtkDataObject* dso = iter->GetCurrentDataObject(); vtkPolyData* pd = vtkPolyData::SafeDownCast(dso); if (!pd || !pd->GetPoints()) { continue; } vtkOpenGLPointGaussianMapperHelper* helper = this->CreateHelper(); this->CopyMapperValuesToHelper(helper); helper->SetInputData(pd); helper->FlatIndex = flatIndex; this->Helpers.push_back(helper); } } else { vtkPolyData* pd = vtkPolyData::SafeDownCast(this->GetInputDataObject(0, 0)); if (pd && pd->GetPoints()) { vtkOpenGLPointGaussianMapperHelper* helper = this->CreateHelper(); this->CopyMapperValuesToHelper(helper); helper->SetInputData(pd); this->Helpers.push_back(helper); } } this->HelperUpdateTime.Modified(); } if (this->Emissive != 0 && !ren->GetSelector()) { vtkOpenGLState* ostate = static_cast(ren)->GetState(); vtkOpenGLState::ScopedglBlendFuncSeparate bfsaver(ostate); ostate->vtkglDepthMask(GL_FALSE); ostate->vtkglBlendFunc(GL_SRC_ALPHA, GL_ONE); // additive for emissive sources this->RenderInternal(ren, actor); } else // intentional else due to scope { this->RenderInternal(ren, actor); } } // this could be made much faster for composite // datasets that have lots of small blocks // but for now we just want to add the functionality void vtkOpenGLPointGaussianMapper::RenderInternal(vtkRenderer* ren, vtkActor* actor) { // Set the PointSize vtkOpenGLRenderWindow* renWin = static_cast(ren->GetRenderWindow()); vtkOpenGLState* ostate = renWin->GetState(); ostate->vtkglPointSize(actor->GetProperty()->GetPointSize()); // render points for point picking in a special way vtkHardwareSelector* selector = ren->GetSelector(); if (selector && selector->GetFieldAssociation() == vtkDataObject::FIELD_ASSOCIATION_POINTS) { static_cast(ren)->GetState()->vtkglDepthMask(GL_FALSE); } if (selector) { selector->BeginRenderProp(); } for (auto hiter = this->Helpers.begin(); hiter != this->Helpers.end(); ++hiter) { // make sure the BOs are up to date vtkOpenGLPointGaussianMapperHelper* helper = *hiter; if (selector && selector->GetCurrentPass() == vtkHardwareSelector::COMPOSITE_INDEX_PASS) { selector->RenderCompositeIndex(helper->FlatIndex); } helper->GaussianRender(ren, actor); } // reset picking if (selector && selector->GetFieldAssociation() == vtkDataObject::FIELD_ASSOCIATION_POINTS) { static_cast(ren)->GetState()->vtkglDepthMask(GL_TRUE); } if (selector) { selector->EndRenderProp(); } this->UpdateProgress(1.0); } vtkOpenGLPointGaussianMapperHelper* vtkOpenGLPointGaussianMapper::CreateHelper() { auto helper = vtkOpenGLPointGaussianMapperHelper::New(); helper->Owner = this; return helper; } void vtkOpenGLPointGaussianMapper::CopyMapperValuesToHelper( vtkOpenGLPointGaussianMapperHelper* helper) { helper->vtkPolyDataMapper::ShallowCopy(this); helper->OpacityTable = this->OpacityTable; helper->OpacityScale = this->OpacityScale; helper->OpacityOffset = this->OpacityOffset; helper->ScaleTable = this->ScaleTable; helper->ScaleScale = this->ScaleScale; helper->ScaleOffset = this->ScaleOffset; helper->Modified(); } //------------------------------------------------------------------------------ void vtkOpenGLPointGaussianMapper::ReleaseGraphicsResources(vtkWindow* win) { for (auto hiter = this->Helpers.begin(); hiter != this->Helpers.end(); ++hiter) { (*hiter)->ReleaseGraphicsResources(win); } this->Modified(); } //------------------------------------------------------------------------------ bool vtkOpenGLPointGaussianMapper::HasTranslucentPolygonalGeometry() { // emissive always needs to be opaque if (this->Emissive) { return false; } return this->Superclass::HasTranslucentPolygonalGeometry(); } //------------------------------------------------------------------------------ void vtkOpenGLPointGaussianMapper::BuildScaleTable() { double range[2]; // if a piecewise function was provided, use it to map the opacities vtkPiecewiseFunction* pwf = this->GetScaleFunction(); int tableSize = this->GetScaleTableSize(); delete[] this->ScaleTable; this->ScaleTable = new float[tableSize + 1]; if (pwf) { // build the interpolation table pwf->GetRange(range); pwf->GetTable(range[0], range[1], tableSize, this->ScaleTable); // duplicate the last value for bilinear interp edge case this->ScaleTable[tableSize] = this->ScaleTable[tableSize - 1]; this->ScaleScale = (tableSize - 1.0) / (range[1] - range[0]); this->ScaleOffset = range[0]; } this->Modified(); } //------------------------------------------------------------------------------ void vtkOpenGLPointGaussianMapper::BuildOpacityTable() { double range[2]; // if a piecewise function was provided, use it to map the opacities vtkPiecewiseFunction* pwf = this->GetScalarOpacityFunction(); int tableSize = this->GetOpacityTableSize(); delete[] this->OpacityTable; this->OpacityTable = new float[tableSize + 1]; if (pwf) { // build the interpolation table pwf->GetRange(range); pwf->GetTable(range[0], range[1], tableSize, this->OpacityTable); // duplicate the last value for bilinear interp edge case this->OpacityTable[tableSize] = this->OpacityTable[tableSize - 1]; this->OpacityScale = (tableSize - 1.0) / (range[1] - range[0]); this->OpacityOffset = range[0]; } this->Modified(); } //------------------------------------------------------------------------------ int vtkOpenGLPointGaussianMapper::FillInputPortInformation( int vtkNotUsed(port), vtkInformation* info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkCompositeDataSet"); return 1; } //------------------------------------------------------------------------------ vtkExecutive* vtkOpenGLPointGaussianMapper::CreateDefaultExecutive() { return vtkCompositeDataPipeline::New(); } //------------------------------------------------------------------------------ // Looks at each DataSet and finds the union of all the bounds void vtkOpenGLPointGaussianMapper::ComputeBounds() { vtkCompositeDataSet* input = vtkCompositeDataSet::SafeDownCast(this->GetInputDataObject(0, 0)); // If we don't have hierarchical data, test to see if we have // plain old polydata. In this case, the bounds are simply // the bounds of the input polydata. if (!input) { this->Superclass::ComputeBounds(); return; } vtkBoundingBox bbox; // for each data set build a vtkPolyDataMapper vtkCompositeDataIterator* iter = input->NewIterator(); iter->GoToFirstItem(); while (!iter->IsDoneWithTraversal()) { vtkPolyData* pd = vtkPolyData::SafeDownCast(iter->GetCurrentDataObject()); if (pd) { double bounds[6]; pd->GetCellsBounds(bounds); bbox.AddBounds(bounds); } iter->GoToNextItem(); } iter->Delete(); bbox.GetBounds(this->Bounds); } //------------------------------------------------------------------------------ void vtkOpenGLPointGaussianMapper::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); } void vtkOpenGLPointGaussianMapper::ProcessSelectorPixelBuffers( vtkHardwareSelector* sel, std::vector& pixeloffsets, vtkProp* prop) { if (sel->GetCurrentPass() == vtkHardwareSelector::ACTOR_PASS) { this->PickPixels.clear(); return; } if (PickPixels.empty() && !pixeloffsets.empty()) { // preprocess the image to find matching pixels and // store them in a map of vectors based on flat index // this makes the block processing far faster as we just // loop over the pixels for our block unsigned char* compositedata = sel->GetRawPixelBuffer(vtkHardwareSelector::COMPOSITE_INDEX_PASS); if (!compositedata) { return; } int maxFlatIndex = 0; for (auto hiter = this->Helpers.begin(); hiter != this->Helpers.end(); ++hiter) { maxFlatIndex = ((*hiter)->FlatIndex > maxFlatIndex) ? (*hiter)->FlatIndex : maxFlatIndex; } this->PickPixels.resize(maxFlatIndex + 1); for (auto pos : pixeloffsets) { int compval = compositedata[pos + 2]; compval = compval << 8; compval |= compositedata[pos + 1]; compval = compval << 8; compval |= compositedata[pos]; compval -= 1; if (compval <= maxFlatIndex) { this->PickPixels[compval].push_back(pos); } } } // for each block update the image for (auto hiter = this->Helpers.begin(); hiter != this->Helpers.end(); ++hiter) { if (!this->PickPixels[(*hiter)->FlatIndex].empty()) { (*hiter)->ProcessSelectorPixelBuffers(sel, this->PickPixels[(*hiter)->FlatIndex], prop); } } }