Commit 2f4ed70c authored by Ken Martin's avatar Ken Martin Committed by Code Review
Browse files

Merge topic 'point_picking' into master

c1eac93b Improve picking support in OpenGL2
parents 8e12d088 c1eac93b
......@@ -39,6 +39,7 @@ vtk_add_test_cxx(${vtk-module}CxxTests tests
TestOrderedTriangulator.cxx
TestOpacity.cxx
TestOSConeCxx.cxx
TestPointSelection.cxx,NO_VALID
TestPolygonSelection.cxx
TestResetCameraVerticalAspectRatio.cxx
TestResetCameraVerticalAspectRatioParallel.cxx
......
/*=========================================================================
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 "vtkTestUtilities.h"
#include "vtkRegressionTestImage.h"
#include "vtkActor.h"
#include "vtkCamera.h"
#include "vtkCommand.h"
#include "vtkHardwareSelector.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkInteractorStyleRubberBandPick.h"
#include "vtkNew.h"
#include "vtkPolyDataMapper.h"
#include "vtkProp3DCollection.h"
#include "vtkRenderedAreaPicker.h"
#include "vtkRenderer.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderWindow.h"
#include "vtkSelection.h"
#include "vtkSelectionNode.h"
#include "vtkSphereSource.h"
class PointPickCommand : public vtkCommand
{
protected:
vtkNew<vtkIdTypeArray> PointIds;
vtkRenderer *Renderer;
vtkAreaPicker *Picker;
vtkPolyDataMapper *Mapper;
public:
static PointPickCommand * New() {return new PointPickCommand;}
vtkTypeMacro(PointPickCommand, vtkCommand);
PointPickCommand()
{
}
virtual ~PointPickCommand()
{
}
void SetPointIds(vtkSelection *selection)
{
// Find selection node that we're interested in:
const vtkIdType numNodes = selection->GetNumberOfNodes();
for (vtkIdType nodeId = 0; nodeId < numNodes; ++nodeId)
{
vtkSelectionNode *node = selection->GetNode(nodeId);
// Check if the mapper is this instance of MoleculeMapper
vtkActor *selActor = vtkActor::SafeDownCast(
node->GetProperties()->Get(vtkSelectionNode::PROP()));
if (selActor && (selActor->GetMapper() == this->Mapper))
{
// Separate the selection ids into atoms and bonds
vtkIdTypeArray *selIds = vtkIdTypeArray::SafeDownCast(
node->GetSelectionList());
if (selIds)
{
vtkIdType numIds = selIds->GetNumberOfTuples();
for (vtkIdType i = 0; i < numIds; ++i)
{
vtkIdType curId = selIds->GetValue(i);
this->PointIds->InsertNextValue(curId);
}
}
}
}
}
vtkIdTypeArray *GetPointIds()
{
return this->PointIds.GetPointer();
}
void SetMapper(vtkPolyDataMapper *m)
{
this->Mapper = m;
}
void SetRenderer(vtkRenderer *r)
{
this->Renderer = r;
}
void SetPicker(vtkAreaPicker *p)
{
this->Picker = p;
}
virtual void Execute(vtkObject *, unsigned long, void *)
{
vtkProp3DCollection *props = this->Picker->GetProp3Ds();
if (props->GetNumberOfItems() != 0)
{
// If anything was picked during the fast area pick, do a more detailed
// pick.
vtkNew<vtkHardwareSelector> selector;
selector->SetFieldAssociation(vtkDataObject::FIELD_ASSOCIATION_POINTS);
selector->SetRenderer(this->Renderer);
selector->SetArea(
static_cast<unsigned int>(this->Renderer->GetPickX1()),
static_cast<unsigned int>(this->Renderer->GetPickY1()),
static_cast<unsigned int>(this->Renderer->GetPickX2()),
static_cast<unsigned int>(this->Renderer->GetPickY2()));
// Make the actual pick and pass the result to the convenience function
// defined earlier
vtkSelection *result = selector->Select();
this->SetPointIds(result);
this->DumpPointSelection();
result->Delete();
}
}
// Convenience function to print out the atom and bond ids that belong to
// molMap and are contained in sel
void DumpPointSelection()
{
// Print selection
cerr << "\n### Selection ###\n";
cerr << "Points: ";
for (vtkIdType i = 0; i < this->PointIds->GetNumberOfTuples(); i++)
{
cerr << this->PointIds->GetValue(i) << " ";
}
cerr << endl;
}
};
int TestPointSelection(int argc, char *argv[])
{
// create a line and a mesh
vtkNew<vtkSphereSource> sphere;
// Set up render engine
vtkNew<vtkPolyDataMapper> sphereMapper;
sphereMapper->SetInputConnection(sphere->GetOutputPort());
vtkNew<vtkActor> actor;
actor->SetMapper(sphereMapper.GetPointer());
vtkNew<vtkRenderer> ren;
ren->AddActor(actor.GetPointer());
vtkNew<vtkRenderWindow> win;
win->SetMultiSamples(0);
win->AddRenderer(ren.GetPointer());
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(win.GetPointer());
ren->SetBackground(0.0,0.0,0.0);
win->SetSize(450,450);
win->Render();
ren->GetActiveCamera()->Zoom(1.2);
// Setup picker
vtkNew<vtkInteractorStyleRubberBandPick> pickerInt;
iren->SetInteractorStyle(pickerInt.GetPointer());
vtkNew<vtkRenderedAreaPicker> picker;
iren->SetPicker(picker.GetPointer());
// We'll follow up the cheap RenderedAreaPick with a detailed selection
// to obtain the atoms and bonds.
vtkNew<PointPickCommand> com;
com->SetRenderer(ren.GetPointer());
com->SetPicker(picker.GetPointer());
com->SetMapper(sphereMapper.GetPointer());
picker->AddObserver(vtkCommand::EndPickEvent, com.GetPointer());
// Make pick -- lower left quarter of renderer
win->Render();
picker->AreaPick(0, 0, 225, 225, ren.GetPointer());
win->Render();
// Interact if desired
int retVal = vtkRegressionTestImage(win.GetPointer());
if ( retVal == vtkRegressionTester::DO_INTERACTOR)
{
iren->Start();
}
// Verify pick
if (com->GetPointIds()->GetNumberOfTuples() < 7 ||
com->GetPointIds()->GetValue(0) != 0 ||
com->GetPointIds()->GetValue(1) != 26 ||
com->GetPointIds()->GetValue(2) != 27 ||
com->GetPointIds()->GetValue(3) != 32 ||
com->GetPointIds()->GetValue(4) != 33 ||
com->GetPointIds()->GetValue(5) != 38 ||
com->GetPointIds()->GetValue(6) != 39
)
{
cerr << "Incorrect atoms/bonds picked! (if any picks were performed inter"
"actively this could be ignored).\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
......@@ -133,7 +133,7 @@ public:
vtkIdType cc=0;
for (idIter = id_values.begin(); idIter != id_values.end(); ++idIter, ++cc)
{
ptr[cc] = *idIter;
ptr[cc] = key.Prop->GetConvertedPickValue(*idIter, fieldassociation);
}
child->SetSelectionList(ids);
ids->FastDelete();
......
......@@ -145,6 +145,14 @@ public:
// \pre keys_can_be_null: requiredKeys==0 || requiredKeys!=0
virtual bool HasKeys(vtkInformation *requiredKeys);
// Description:
// Props may provide a mapping from picked value to actual value
// This is useful for hardware based pickers where
// there is a mapping between the color in the buffer
// and the actual pick value
virtual vtkIdType GetConvertedPickValue(vtkIdType idIn,
int vtkNotUsed(fieldassociation)) { return idIn; }
//BTX
// Description:
// WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE
......
......@@ -18,6 +18,7 @@
#include "vtkMatrix3x3.h"
#include "vtkMatrix4x4.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLPolyDataMapper.h"
#include "vtkOpenGLRenderer.h"
#include "vtkProperty.h"
#include "vtkOpenGLError.h"
......@@ -124,3 +125,13 @@ void vtkOpenGLActor::GetKeyMatrices(vtkMatrix4x4 *&mcwc, vtkMatrix3x3 *&normMat)
mcwc = this->MCWCMatrix;
normMat = this->NormalMatrix;
}
vtkIdType vtkOpenGLActor::GetConvertedPickValue(vtkIdType idIn, int fieldassociation)
{
vtkOpenGLPolyDataMapper *pdm = vtkOpenGLPolyDataMapper::SafeDownCast(this->GetMapper());
if (pdm)
{
return pdm->GetConvertedPickValue(idIn, fieldassociation,this);
}
return idIn;
}
......@@ -40,6 +40,13 @@ public:
void GetKeyMatrices(vtkMatrix4x4 *&WCVCMatrix, vtkMatrix3x3 *&normalMatrix);
// Description:
// Props may provide a mapping from picked value to actual value
// This is useful for hardware based pickers where
// there is a mapping between the color in the buffer
// and the actual pick value
virtual vtkIdType GetConvertedPickValue(vtkIdType idIn, int fieldassociation);
protected:
vtkOpenGLActor();
~vtkOpenGLActor();
......
......@@ -1003,10 +1003,25 @@ void vtkOpenGLPolyDataMapper::SetPropertyShaderParameters(vtkgl::CellBO &cellBO,
//-----------------------------------------------------------------------------
void vtkOpenGLPolyDataMapper::RenderPieceStart(vtkRenderer* ren, vtkActor *actor)
{
// Set the PointSize and LineWidget
#if GL_ES_VERSION_2_0 != 1
glPointSize(actor->GetProperty()->GetPointSize()); // not on ES2
#endif
glLineWidth(actor->GetProperty()->GetLineWidth()); // supported by all OpenGL versions
vtkHardwareSelector* selector = ren->GetSelector();
if (selector && this->PopulateSelectionSettings)
{
selector->BeginRenderProp();
// render points for point picking in a special way
if (selector->GetFieldAssociation() == vtkDataObject::FIELD_ASSOCIATION_POINTS &&
selector->GetCurrentPass() > vtkHardwareSelector::ACTOR_PASS)
{
glPointSize(4.0); //make verts large enough to be sure to overlap cell
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0,2.0); // supported on ES2/3/etc
glDepthMask(GL_FALSE); //prevent verts from interfering with each other
}
if (selector->GetCurrentPass() == vtkHardwareSelector::COMPOSITE_INDEX_PASS)
{
selector->RenderCompositeIndex(1);
......@@ -1022,16 +1037,17 @@ void vtkOpenGLPolyDataMapper::RenderPieceStart(vtkRenderer* ren, vtkActor *actor
this->TimeToDraw = 0.0;
this->pickingAttributeIDOffset = 0;
bool picking = (ren->GetIsPicking() || selector != NULL);
// Update the OpenGL if needed.
if (this->OpenGLUpdateTime < this->GetMTime() ||
this->OpenGLUpdateTime < actor->GetMTime() ||
this->OpenGLUpdateTime < this->CurrentInput->GetMTime() )
this->OpenGLUpdateTime < this->CurrentInput->GetMTime() ||
this->LastSelectionState || picking)
{
this->UpdateVBO(ren, actor);
this->OpenGLUpdateTime.Modified();
}
// If we are coloring by texture, then load the texture map.
// Use Map as indicator, because texture hangs around.
if (this->InternalColorTexture)
......@@ -1044,12 +1060,6 @@ void vtkOpenGLPolyDataMapper::RenderPieceStart(vtkRenderer* ren, vtkActor *actor
this->LastBoundBO = NULL;
// Set the PointSize and LineWidget
#if GL_ES_VERSION_2_0 != 1
glPointSize(actor->GetProperty()->GetPointSize()); // not on ES2
#endif
glLineWidth(actor->GetProperty()->GetLineWidth()); // supported by all OpenGL versions
if ( this->GetResolveCoincidentTopology() )
{
glEnable(GL_POLYGON_OFFSET_FILL);
......@@ -1068,6 +1078,7 @@ void vtkOpenGLPolyDataMapper::RenderPieceStart(vtkRenderer* ren, vtkActor *actor
glPolygonOffset(f,u); // supported on ES2/3/etc
}
}
}
//-----------------------------------------------------------------------------
......@@ -1090,12 +1101,24 @@ void vtkOpenGLPolyDataMapper::RenderPieceDraw(vtkRenderer* ren, vtkActor *actor)
this->pickingAttributeIDOffset += (int)this->Points.indexCount;
}
int representation = actor->GetProperty()->GetRepresentation();
// render points for point picking in a special way
// all cell types should be rendered as points
vtkHardwareSelector* selector = ren->GetSelector();
if (selector && this->PopulateSelectionSettings &&
selector->GetFieldAssociation() == vtkDataObject::FIELD_ASSOCIATION_POINTS &&
selector->GetCurrentPass() > vtkHardwareSelector::ACTOR_PASS)
{
representation = VTK_POINTS;
}
// draw lines
if (this->Lines.indexCount)
{
this->UpdateShader(this->Lines, ren, actor);
this->Lines.ibo.Bind();
if (actor->GetProperty()->GetRepresentation() == VTK_POINTS)
if (representation == VTK_POINTS)
{
glDrawRangeElements(GL_POINTS, 0,
static_cast<GLuint>(layout.VertexCount - 1),
......@@ -1121,8 +1144,8 @@ void vtkOpenGLPolyDataMapper::RenderPieceDraw(vtkRenderer* ren, vtkActor *actor)
// First we do the triangles, update the shader, set uniforms, etc.
this->UpdateShader(this->Tris, ren, actor);
this->Tris.ibo.Bind();
GLenum mode = (actor->GetProperty()->GetRepresentation() == VTK_POINTS) ? GL_POINTS :
(actor->GetProperty()->GetRepresentation() == VTK_WIREFRAME) ? GL_LINES : GL_TRIANGLES;
GLenum mode = (representation == VTK_POINTS) ? GL_POINTS :
(representation == VTK_WIREFRAME) ? GL_LINES : GL_TRIANGLES;
glDrawRangeElements(mode, 0,
static_cast<GLuint>(layout.VertexCount - 1),
static_cast<GLsizei>(this->Tris.indexCount),
......@@ -1138,7 +1161,7 @@ void vtkOpenGLPolyDataMapper::RenderPieceDraw(vtkRenderer* ren, vtkActor *actor)
// Use the tris shader program/VAO, but triStrips ibo.
this->UpdateShader(this->TriStrips, ren, actor);
this->TriStrips.ibo.Bind();
if (actor->GetProperty()->GetRepresentation() == VTK_POINTS)
if (representation == VTK_POINTS)
{
glDrawRangeElements(GL_POINTS, 0,
static_cast<GLuint>(layout.VertexCount - 1),
......@@ -1146,7 +1169,7 @@ void vtkOpenGLPolyDataMapper::RenderPieceDraw(vtkRenderer* ren, vtkActor *actor)
GL_UNSIGNED_INT,
reinterpret_cast<const GLvoid *>(NULL));
}
if (actor->GetProperty()->GetRepresentation() == VTK_WIREFRAME)
if (representation == VTK_WIREFRAME)
{
glMultiDrawElements(GL_LINE_STRIP,
(GLsizei *)(&this->TriStrips.elementsArray[0]),
......@@ -1154,7 +1177,7 @@ void vtkOpenGLPolyDataMapper::RenderPieceDraw(vtkRenderer* ren, vtkActor *actor)
reinterpret_cast<const GLvoid **>(&(this->TriStrips.offsetArray[0])),
(GLsizei)this->TriStrips.offsetArray.size());
}
if (actor->GetProperty()->GetRepresentation() == VTK_SURFACE)
if (representation == VTK_SURFACE)
{
glMultiDrawElements(GL_TRIANGLE_STRIP,
(GLsizei *)(&this->TriStrips.elementsArray[0]),
......@@ -1173,6 +1196,13 @@ void vtkOpenGLPolyDataMapper::RenderPieceFinish(vtkRenderer* ren, vtkActor *vtkN
vtkHardwareSelector* selector = ren->GetSelector();
if (selector && this->PopulateSelectionSettings)
{
// render points for point picking in a special way
if (selector->GetFieldAssociation() == vtkDataObject::FIELD_ASSOCIATION_POINTS &&
selector->GetCurrentPass() > vtkHardwareSelector::ACTOR_PASS)
{
glDepthMask(GL_TRUE);
glDisable(GL_POLYGON_OFFSET_FILL);
}
selector->EndRenderProp();
}
......@@ -1325,7 +1355,7 @@ void vtkOpenGLPolyDataMapper::ComputeBounds()
}
//-------------------------------------------------------------------------
void vtkOpenGLPolyDataMapper::UpdateVBO(vtkRenderer *vtkNotUsed(ren), vtkActor *act)
void vtkOpenGLPolyDataMapper::UpdateVBO(vtkRenderer *ren, vtkActor *act)
{
vtkPolyData *poly = this->CurrentInput;
......@@ -1428,7 +1458,17 @@ void vtkOpenGLPolyDataMapper::UpdateVBO(vtkRenderer *vtkNotUsed(ren), vtkActor *
this->Points.indexCount = CreatePointIndexBuffer(prims[0],
this->Points.ibo);
if (act->GetProperty()->GetRepresentation() == VTK_POINTS)
int representation = act->GetProperty()->GetRepresentation();
vtkHardwareSelector* selector = ren->GetSelector();
if (selector && this->PopulateSelectionSettings &&
selector->GetFieldAssociation() == vtkDataObject::FIELD_ASSOCIATION_POINTS &&
selector->GetCurrentPass() > vtkHardwareSelector::ACTOR_PASS)
{
representation = VTK_POINTS;
}
if (representation == VTK_POINTS)
{
this->Lines.indexCount = CreatePointIndexBuffer(prims[1],
this->Lines.ibo);
......@@ -1445,7 +1485,7 @@ void vtkOpenGLPolyDataMapper::UpdateVBO(vtkRenderer *vtkNotUsed(ren), vtkActor *
this->Lines.offsetArray,
this->Lines.elementsArray, false);
if (act->GetProperty()->GetRepresentation() == VTK_WIREFRAME)
if (representation == VTK_WIREFRAME)
{
vtkDataArray *ef = poly->GetPointData()->GetAttribute(
vtkDataSetAttributes::EDGEFLAG);
......@@ -1580,6 +1620,148 @@ bool vtkOpenGLPolyDataMapper::GetIsOpaque()
return this->Superclass::GetIsOpaque();
}
vtkIdType vtkOpenGLPolyDataMapper::GetConvertedPickValue(vtkIdType idIn, int fieldassociation, vtkActor *act)
{
vtkPolyData *poly = this->CurrentInput;
vtkCellArray *prims[4];
prims[0] = poly->GetVerts();
prims[1] = poly->GetLines();
prims[2] = poly->GetPolys();
prims[3] = poly->GetStrips();
vtkIdType* indices(NULL);
vtkIdType npts(0);
vtkIdType localId = idIn;
// handle cell picking
if (fieldassociation == vtkDataObject::FIELD_ASSOCIATION_CELLS)
{
// for points the cell is the cell, easy peasy
if (static_cast<size_t>(localId) < this->Points.indexCount)
{
return localId;
}
localId -= this->Points.indexCount;
vtkIdType offset = 0; // adjustment between OpenGL cells and VTK cells
int representation = act->GetProperty()->GetRepresentation();
// for lines the cell has to be computed because we do not
// know how many line segments are in the polyline
if (this->Lines.indexCount > 0)
{
// compute the location in the cell array
vtkIdType cellCount = 0;
for (prims[1]->InitTraversal(); prims[1]->GetNextCell(npts, indices); )
{
vtkIdType numCells = (representation == VTK_POINTS) ? npts : (npts - 1);
if (localId < cellCount + numCells)
{
return localId + offset;
}
offset = offset + numCells - 1;
cellCount += numCells;
}
localId -= (this->Lines.indexCount/2);
}
// for polys the cell has to be computed because we do not
// know how many triangles are in the poly
if (this->Tris.indexCount > 0)
{
// compute the location in the cell array
vtkIdType cellCount = 0;
for (prims[2]->InitTraversal(); prims[2]->GetNextCell(npts, indices); )
{
vtkIdType numCells = (representation == VTK_POINTS) ? npts :
(representation == VTK_WIREFRAME) ? npts : (npts - 2);
if (localId < cellCount + numCells)
{
return localId + offset;
}
offset = offset + numCells - 1;
cellCount += numCells;
}
localId -= (this->Tris.indexCount/3);
}
// for strips the cell maps exactly, easy peasy
if (static_cast<size_t>(localId) < this->TriStrips.indexCount)
{
return localId;
}
return 0;
}
// if we got here, then it is point based picking
// is it a point?
if (static_cast<size_t>(localId) < this->Points.indexCount)
{
prims[0]->GetCell(localId,npts,indices);
return indices[0];
}
localId -= this->Points.indexCount;
// when picking in point mode, we render all primitives as
// points. The graphics hardware tells us what point was
// picked. e.g. the 11th point. We have to convert that into
// a point ID. This can be done by traversing the cell
// arrays to find the 11th point rendered. But that operation
// can be expensive for the more complex cell arrays.
// You could speed this up significantly by building a
// monotonically increasing array of indexes that are
// not points in the cells array. Then a binary search
// would let you index quickly into the right array location.
// is it a line?
if (static_cast<size_t>(localId) < this->Lines.indexCount)
{
// compute the location in the cell array
vtkIdType pointCount = 0;
for (prims[1]->InitTraversal(); prims[1]->GetNextCell(npts, indices); )
{
if (localId < pointCount + npts)
{
return indices[localId - pointCount];
}
pointCount += npts;
}
}
localId -= this->Lines.indexCount;
// is it a poly
if (static_cast<size_t>(localId) < this->Tris.indexCount)
{
// compute the location in the cell array
vtkIdType pointCount = 0;
for (prims[2]->InitTraversal(); prims[2]->GetNextCell(npts, indices); )
{
if (localId < pointCount + npts)
{
return indices[localId - pointCount];
}
pointCount += npts;
}
}
localId -= this->Tris.indexCount;
// is it a strip?
if (static_cast<size_t>(localId) < this->TriStrips.indexCount)
{
// compute the location in the cell array
vtkIdType pointCount = 0;
for (prims[3]->InitTraversal(); prims[3]->GetNextCell(npts, indices); )
{
if (localId < pointCount + npts)