// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #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_glad.h" #include "vtkOpenGLPointGaussianMapperHelper.h" #include #include VTK_ABI_NAMESPACE_BEGIN //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkOpenGLPointGaussianMapperHelper); //------------------------------------------------------------------------------ vtkOpenGLPointGaussianMapperHelper::vtkOpenGLPointGaussianMapperHelper() { this->Owner = nullptr; this->UsingPoints = false; this->BoundScale = 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::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "UsingPoints: " << (this->UsingPoints ? "true" : "false") << endl; os << indent << "BoundScale: " << this->BoundScale << endl; os << indent << "FlatIndex: " << this->FlatIndex << endl; os << indent << "OpacityScale: " << this->OpacityScale << endl; os << indent << "ScaleScale: " << this->ScaleScale << endl; os << indent << "OpacityOffset: " << this->OpacityOffset << endl; os << indent << "ScaleOffset: " << this->ScaleOffset << endl; } //------------------------------------------------------------------------------ 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;"); if (this->Owner->GetAnisotropic()) { vtkShaderProgram::Substitute(VSSource, "//VTK::Covariance::Dec", "in vec3 radiusMC;\n" "in vec4 rotationMC;"); vtkShaderProgram::Substitute(VSSource, "//VTK::Covariance::Impl", "mat3 cov = T * computeCov3D(radiusMC, rotationMC) * transpose(T);"); } else { vtkShaderProgram::Substitute(VSSource, "//VTK::Covariance::Dec", "in float radiusMC;"); vtkShaderProgram::Substitute(VSSource, "//VTK::Covariance::Impl", "float radius = scaleFactor * radiusMC;\n" "mat3 cov = (radius * radius) * T * transpose(T);"); } 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;", false); } shaders[vtkShader::Fragment]->SetSource(FSSource); } this->Superclass::ReplaceShaderColor(shaders, ren, actor); // std::cerr << shaders[vtkShader::Fragment]->GetSource() << endl; } //------------------------------------------------------------------------------ bool vtkOpenGLPointGaussianMapperHelper::GetNeedToRebuildShaders( vtkOpenGLHelper& cellBO, vtkRenderer* ren, vtkActor* actor) { this->PrimitiveInfo[&cellBO].LastLightComplexity = primitiveInfo::NoLighting; 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("boundScale", this->BoundScale); cellBO.Program->SetUniformf("scaleFactor", this->Owner->GetScaleFactor()); cellBO.Program->SetUniform3f("lowpassMatrix", this->Owner->GetLowpassMatrix()); } 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) { unsigned char* vPtr = outColors->GetPointer(0); 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]; } } return static_cast(radius); } template void vtkOpenGLPointGaussianMapperHelperSizes(vtkFloatArray* scales, PointDataType* sizes, int nComponent, int component, vtkIdType numPts, vtkOpenGLPointGaussianMapperHelper* self) { float* it = scales->GetPointer(0); 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->BoundScale = this->Owner->GetBoundScale(); 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(); } this->VBOs->CacheDataArray("vertexMC", poly->GetPoints()->GetData(), ren, VTK_FLOAT); if (this->Owner->GetAnisotropic()) { this->VBOs->CacheDataArray( "radiusMC", poly->GetPointData()->GetArray(this->Owner->GetScaleArray()), ren, VTK_FLOAT); this->VBOs->CacheDataArray("rotationMC", poly->GetPointData()->GetArray(this->Owner->GetRotationArray()), ren, VTK_FLOAT); } else if (!this->UsingPoints) { vtkNew offsets; 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)); } } else { vtkOpenGLPointGaussianMapperHelperSizes( offsets, static_cast(nullptr), 0, 0, poly->GetPoints()->GetNumberOfPoints(), this); } this->VBOs->CacheDataArray("radiusMC", offsets, ren, VTK_FLOAT); } else { this->VBOs->CacheDataArray("radiusMC", nullptr, ren, VTK_FLOAT); } if (this->Colors) { vtkNew clrs; clrs->SetNumberOfComponents(4); clrs->SetNumberOfTuples(splatCount); vtkOpenGLPointGaussianMapperHelperColors(clrs, poly->GetPoints()->GetNumberOfPoints(), this->Colors ? this->Colors->GetPointer(0) : nullptr, this->Colors ? this->Colors->GetNumberOfComponents() : 0, hasOpacityArray ? poly->GetPointData()->GetArray(this->Owner->GetOpacityArray()) : nullptr, this->Owner->GetOpacityArrayComponent(), this); this->VBOs->CacheDataArray("scalarColor", clrs, ren, VTK_UNSIGNED_CHAR); } this->VBOs->BuildAllVBOs(ren); // reset all IBOs for (int i = PrimitiveStart; i < PrimitiveEnd; i++) { this->Primitives[i].IBO->IndexCount = 0; } if (poly->GetVerts()->GetNumberOfCells() > 0) { this->Primitives[PrimitivePoints].IBO->CreatePointIndexBuffer(poly->GetVerts()); } else { std::vector verts(splatCount); std::iota(verts.begin(), verts.end(), 0); this->Primitives[PrimitivePoints].IBO->Upload( verts, vtkOpenGLIndexBufferObject::ElementArrayBuffer); this->Primitives[PrimitivePoints].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[PrimitivePoints], ren, actor); this->Primitives[PrimitivePoints].IBO->Bind(); glDrawRangeElements(GL_POINTS, 0, static_cast(numVerts - 1), static_cast(this->Primitives[PrimitivePoints].IBO->IndexCount), GL_UNSIGNED_INT, nullptr); this->Primitives[PrimitivePoints].IBO->Release(); } } 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); } } } VTK_ABI_NAMESPACE_END