Commit 400c0987 authored by Alexis Girault's avatar Alexis Girault

ENH: Use no copy data array to map vertices to VTK

Update VTK mesh render delegates (SurfaceMesh,
LineMesh, TetrahedralMesh) to directly map the vertices
from the eigen and stl structures in iMSTK, and get rid
of VTKMappedVertexArray. This should considerably reduce
overhead calls and improve performances thanks to the
no-copy operation.

See http://www.vtk.org/Wiki/VTK/Tutorials/DataArrays

Add back normals generationin the SurfaceMesh render delegate.

Also updates testPbdCloth to add another row of points
to the cloth (made it easier to debug the initial
vertex positions), as well as add lights to better
debug normals.
parent 0fa25818
......@@ -21,16 +21,12 @@
#include "imstkVTKLineMeshRenderDelegate.h"
#include <vtkTrivialProducer.h>
#include "imstkLineMesh.h"
#include <vtkLineSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkPointData.h>
#include <vtkFloatArray.h>
#include <vtkPoints.h>
#include <vtkDoubleArray.h>
#include <vtkImageReader2Factory.h>
#include <vtkImageReader2.h>
#include <vtkTexture.h>
#include <vtkProperty.h>
#include "g3log/g3log.hpp"
......@@ -41,55 +37,39 @@ VTKLineMeshRenderDelegate::VTKLineMeshRenderDelegate(std::shared_ptr<LineMesh> l
m_geometry(lineMesh)
{
// Map vertices
m_mappedVertexArray = vtkDoubleArray::New();
StdVectorOfVec3d& vertices = m_geometry->getVerticesPositionsNotConst();
double* vertData = reinterpret_cast<double*>(vertices.data());
m_mappedVertexArray = vtkSmartPointer<vtkDoubleArray>::New();
m_mappedVertexArray->SetNumberOfComponents(3);
auto vertices = m_geometry->getVerticesPositionsNotConst();
for (size_t i = 0; i < vertices.size(); ++i) {
m_mappedVertexArray->InsertNextTuple3(vertices[i][0], vertices[i][1], vertices[i][2]);
}
this->mapVertices();
m_mappedVertexArray->SetArray(vertData, vertices.size()*3, 1);
// Create points
auto points = vtkSmartPointer<vtkPoints>::New();
points->SetNumberOfPoints(m_geometry->getNumVertices());
points->SetData(m_mappedVertexArray);
m_lines = vtkSmartPointer<vtkLineSource>::New();
m_lines->SetPoints(points);
m_lines->Update();
// Create line
auto lines = vtkSmartPointer<vtkLineSource>::New();
lines->SetPoints(points);
//Create a mapper and actor
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(m_lines->GetOutputPort());
auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(lines->GetOutputPort());
// Actor
m_actor->SetMapper(mapper);
// Transform
this->updateActorTransform();
}
void
VTKLineMeshRenderDelegate::mapVertices()
{
auto vertices = m_geometry->getVerticesPositionsNotConst();
for (size_t i = 0; i < vertices.size(); ++i) {
m_mappedVertexArray->SetTuple3(i, vertices[i][0], vertices[i][1], vertices[i][2]);
}
// TODO: only when vertices modified
m_mappedVertexArray->Modified();
}
void
VTKLineMeshRenderDelegate::update()
{
// Base class update
VTKRenderDelegate::update();
this->mapVertices();
m_lines->Update();
m_mappedVertexArray->Modified(); // TODO: only modify if vertices change
}
std::shared_ptr<Geometry>
......
......@@ -25,14 +25,14 @@
#include <memory>
#include "imstkVTKRenderDelegate.h"
#include "imstkLineMesh.h"
#include <vtkLineSource.h>
#include "vtkPolyData.h"
#include "imstkVTKMappedVertexArray.h"
class vtkDoubleArray;
namespace imstk
{
class LineMesh;
///
/// \class LineMeshRenderDelegate
///
......@@ -49,12 +49,7 @@ public:
///
/// \brief
///
VTKLineMeshRenderDelegate(std::shared_ptr<LineMesh>LineMesh);
///
/// \brief
///
void mapVertices();
VTKLineMeshRenderDelegate(std::shared_ptr<LineMesh> lineMesh);
///
/// \brief
......@@ -67,9 +62,9 @@ public:
std::shared_ptr<Geometry>getGeometry() const override;
protected:
vtkSmartPointer<vtkLineSource> m_lines;
std::shared_ptr<LineMesh> m_geometry;
vtkSmartPointer<vtkDoubleArray> m_mappedVertexArray;
std::shared_ptr<LineMesh> m_geometry; ///> Geometry to render
vtkSmartPointer<vtkDoubleArray> m_mappedVertexArray; ///> Mapped array of vertices
};
......
/*=========================================================================
Library: iMSTK
Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
& Imaging in Medicine, Rensselaer Polytechnic Institute.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.txt
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=========================================================================*/
#ifndef imstkVTKMappedVertexArray_h
#define imstkVTKMappedVertexArray_h
#include "vtkMappedDataArray.h"
#include "vtkObjectFactory.h" // for vtkStandardNewMacro
// imstk
#include "imstkMath.h"
// STL includes
#include <array>
namespace imstk
{
///
/// \class MappedVertexArray
///
/// \brief
///
class VTKMappedVertexArray: public vtkMappedDataArray<double>
{
public:
static VTKMappedVertexArray *New();
vtkTypeMacro(VTKMappedVertexArray,vtkMappedDataArray);
virtual void PrintSelf(ostream &os, vtkIndent indent);
// Description:
// Set the raw scalar arrays for the coordinate set. This class takes
// ownership of the arrays and deletes them with delete[].
void SetVertexArray(StdVectorOfVec3d &vertices);
// Reimplemented virtuals -- see superclasses for descriptions:
void Initialize();
void GetTuples(vtkIdList *ptIds, vtkAbstractArray *output);
void GetTuples(vtkIdType p1, vtkIdType p2, vtkAbstractArray *output);
void Squeeze();
vtkArrayIterator *NewIterator();
vtkIdType LookupValue(vtkVariant value);
void LookupValue(vtkVariant value, vtkIdList *ids);
vtkVariant GetVariantValue(vtkIdType idx);
void ClearLookup();
double* GetTuple(vtkIdType i);
void GetTuple(vtkIdType i, double *tuple);
vtkIdType LookupTypedValue(double value);
void LookupTypedValue(double value, vtkIdList *ids);
double GetValue(vtkIdType idx) const;
double& GetValueReference(vtkIdType idx);
void GetTypedTuple(vtkIdType idx, ValueType *t) const;
void GetTupleValue(vtkIdType idx, double *t);
// Description:
// This container is read only -- this method does nothing but print a
// warning.
int Allocate(vtkIdType sz, vtkIdType ext);
int Resize(vtkIdType numTuples);
void SetNumberOfTuples(vtkIdType number);
void SetTuple(vtkIdType i, vtkIdType j, vtkAbstractArray *source);
void SetTuple(vtkIdType i, const float *source);
void SetTuple(vtkIdType i, const double *source);
void InsertTuple(vtkIdType i, vtkIdType j, vtkAbstractArray *source);
void InsertTuple(vtkIdType i, const float *source);
void InsertTuple(vtkIdType i, const double *source);
void InsertTuples(vtkIdList *dstIds, vtkIdList *srcIds,
vtkAbstractArray *source);
void InsertTuples(vtkIdType dstStart, vtkIdType n, vtkIdType srcStart,
vtkAbstractArray* source);
vtkIdType InsertNextTuple(vtkIdType j, vtkAbstractArray *source);
vtkIdType InsertNextTuple(const float *source);
vtkIdType InsertNextTuple(const double *source);
void DeepCopy(vtkAbstractArray *aa);
void DeepCopy(vtkDataArray *da);
void InterpolateTuple(vtkIdType i, vtkIdList *ptIndices,
vtkAbstractArray* source, double* weights);
void InterpolateTuple(vtkIdType i, vtkIdType id1, vtkAbstractArray *source1,
vtkIdType id2, vtkAbstractArray *source2, double t);
void SetVariantValue(vtkIdType idx, vtkVariant value);
void RemoveTuple(vtkIdType id);
void RemoveFirstTuple();
void RemoveLastTuple();
void SetTupleValue(vtkIdType i, const double *t);
void InsertTupleValue(vtkIdType i, const double *t);
vtkIdType InsertNextTupleValue(const double *t);
void SetValue(vtkIdType idx, double value);
vtkIdType InsertNextValue(double v);
void InsertValue(vtkIdType idx, double v);
void InsertVariantValue(vtkIdType idx, vtkVariant value);
void SetTypedTuple(vtkIdType i, const ValueType *t) {}
void InsertTypedTuple(vtkIdType i, const ValueType *t) {}
vtkIdType InsertNextTypedTuple(const ValueType *t) { return 0; }
protected:
///
/// \brief Constructor
///
VTKMappedVertexArray() {}
///
/// \brief Destructor
///
~VTKMappedVertexArray() {}
StdVectorOfVec3d *vertexArray; ///>
private:
VTKMappedVertexArray(const VTKMappedVertexArray &); // Not implemented.
void operator=(const VTKMappedVertexArray &); // Not implemented.
///
/// \brief
///
vtkIdType Lookup(const double &val, vtkIdType startIndex);
Vec3d TempDoubleArray; ///>
};
} // imstk
#endif // ifndef imstkMappedVertexArray_h
......@@ -21,11 +21,16 @@
#include "imstkVTKSurfaceMeshRenderDelegate.h"
#include <vtkTrivialProducer.h>
#include "imstkSurfaceMesh.h"
#include <vtkPolyData.h>
#include <vtkPolyDataNormals.h>
#include <vtkPolyDataMapper.h>
#include <vtkPointData.h>
#include <vtkFloatArray.h>
#include <vtkPoints.h>
#include <vtkDoubleArray.h>
#include <vtkCellArray.h>
#include <vtkFloatArray.h>
#include <vtkPointData.h>
#include <vtkImageReader2Factory.h>
#include <vtkImageReader2.h>
#include <vtkTexture.h>
......@@ -40,38 +45,42 @@ VTKSurfaceMeshRenderDelegate::VTKSurfaceMeshRenderDelegate(std::shared_ptr<Surfa
m_geometry(surfaceMesh)
{
// Map vertices
m_mappedVertexArray = vtkDoubleArray::New();
StdVectorOfVec3d& vertices = m_geometry->getVerticesPositionsNotConst();
double* vertData = reinterpret_cast<double*>(vertices.data());
  • Answer to previously posted message by @NickMilef:

    Is this data packed into the VBO as a double?

    No, that does not change how the VBO is filled in VTK (see here) which is floats, but the reinterpret_cast needs to look at the data as an array of doubleto correctly read the data allocated in memory since the vertices are currently stored in a std::vector<Eigen::Vector3d>, 'd' being for double.

    If there are no need for double in the simulation on the physics side ( @sreekanth-arikatla ?) , I assume we could switch our data structures to float to save on memory and perfs. Was that what you were thinking @NickMilef ?

    Edited by Alexis Girault
  • I deleted my comment before you replied because I found out how they did them in VTK. I was concerned about the vertex shader performance with using doubles on the GPU side because that part is really lacking in modern architectures. Floats can be better for performance if they are precise enough on the CPU side as well, but it generally depends how much data you're processing though.

    Edited by Nicholas Milef
  • yep, I still answered because I figured that'd be a good conversation to have :). What's your take on that @sreekanth-arikatla ? Can we live with floats in iMSTK for your part of things?

    Edited by Alexis Girault
  • Generally double is preferred for deformation computations (it matters more in some cases than other). But if you just took the data from double precision physics engine as input and do all the rendering related computations with single precision, it should just be fine.

  • Good then. That's how it currently is. Thank you.

Please register or sign in to reply
m_mappedVertexArray = vtkSmartPointer<vtkDoubleArray>::New();
m_mappedVertexArray->SetNumberOfComponents(3);
auto vertices = m_geometry->getVerticesPositionsNotConst();
for (size_t i = 0; i < vertices.size(); ++i) {
m_mappedVertexArray->InsertNextTuple3(vertices[i][0], vertices[i][1], vertices[i][2]);
}
this->mapVertices();
m_mappedVertexArray->SetArray(vertData, vertices.size()*3, 1);
// Create points
auto points = vtkSmartPointer<vtkPoints>::New();
points->SetNumberOfPoints(m_geometry->getNumVertices());
points->SetData(m_mappedVertexArray);
// Copy triangles
auto triangles = vtkSmartPointer<vtkCellArray>::New();
// Copy cells
auto cells = vtkSmartPointer<vtkCellArray>::New();
vtkIdType cell[3];
for(const auto &t : m_geometry->getTrianglesVertices())
{
cell[0] = t[0];
cell[1] = t[1];
cell[2] = t[2];
triangles->InsertNextCell(3,cell);
for(size_t i = 0; i < 3; ++i)
{
cell[i] = t[i];
}
cells->InsertNextCell(3,cell);
}
// Create PolyData
auto polydata = vtkSmartPointer<vtkPolyData>::New();
polydata->SetPoints(points);
polydata->SetPolys(triangles);
polydata->SetPolys(cells);
// Compute Normals
auto normalGen = vtkSmartPointer<vtkPolyDataNormals>::New();
normalGen->SetInputData(polydata);
normalGen->SplittingOff();
// Mapper
auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputData(polydata);
mapper->SetInputConnection(normalGen->GetOutputPort());
// Copy textures
int unit = 0;
......@@ -114,18 +123,6 @@ VTKSurfaceMeshRenderDelegate::VTKSurfaceMeshRenderDelegate(std::shared_ptr<Surfa
unit++;
}
//// Update normals
//auto normals = surfaceMesh->getPointDataArray("Normals");
//auto vtkNormals = vtkSmartPointer<vtkFloatArray>::New();
//vtkNormals->SetNumberOfComponents(3);
//vtkNormals->SetName("Normals");
//for (auto const normal : normals)
//{
// double triple[3] = { normal[0], normal[1], normal[2] };
// vtkNormals->InsertNextTuple(triple);
//}
//polydata->GetPointData()->SetNormals(vtkNormals);
// Actor
m_actor->SetMapper(mapper);
......@@ -133,26 +130,13 @@ VTKSurfaceMeshRenderDelegate::VTKSurfaceMeshRenderDelegate(std::shared_ptr<Surfa
this->updateActorTransform();
}
void
VTKSurfaceMeshRenderDelegate::mapVertices()
{
auto vertices = m_geometry->getVerticesPositionsNotConst();
for (size_t i = 0; i < vertices.size(); ++i) {
m_mappedVertexArray->SetTuple3(i, vertices[i][0], vertices[i][1], vertices[i][2]);
}
// TODO: only when vertices modified
m_mappedVertexArray->Modified();
}
void
VTKSurfaceMeshRenderDelegate::update()
{
// Base class update
VTKRenderDelegate::update();
this->mapVertices();
m_mappedVertexArray->Modified(); // TODO: only modify if vertices change
}
std::shared_ptr<Geometry>
......
......@@ -25,14 +25,14 @@
#include <memory>
#include "imstkVTKRenderDelegate.h"
#include "imstkSurfaceMesh.h"
#include "vtkPolyData.h"
#include "imstkVTKMappedVertexArray.h"
class vtkDoubleArray;
namespace imstk
{
class SurfaceMesh;
///
/// \class SurfaceMeshRenderDelegate
///
......@@ -49,13 +49,7 @@ public:
///
/// \brief
///
VTKSurfaceMeshRenderDelegate(std::shared_ptr<SurfaceMesh>SurfaceMesh);
///
/// \brief
///
void mapVertices();
VTKSurfaceMeshRenderDelegate(std::shared_ptr<SurfaceMesh> surfaceMesh);
///
/// \brief
......@@ -65,12 +59,12 @@ public:
///
/// \brief
///
std::shared_ptr<Geometry>getGeometry() const override;
std::shared_ptr<Geometry> getGeometry() const override;
protected:
std::shared_ptr<SurfaceMesh> m_geometry;
vtkSmartPointer<vtkDoubleArray> m_mappedVertexArray;
std::shared_ptr<SurfaceMesh> m_geometry; ///> Geometry to render
vtkSmartPointer<vtkDoubleArray> m_mappedVertexArray; ///> Mapped array of vertices
};
......
......@@ -21,11 +21,13 @@
#include "imstkVTKTetrahedralMeshRenderDelegate.h"
#include "imstkVTKMappedVertexArray.h"
#include "imstkTetrahedralMesh.h"
#include <vtkCellArray.h>
#include <vtkTrivialProducer.h>
#include <vtkUnstructuredGrid.h>
#include <vtkDataSetMapper.h>
#include <vtkPoints.h>
#include <vtkDoubleArray.h>
#include <vtkCellArray.h>
#include "g3log/g3log.hpp"
......@@ -35,46 +37,56 @@ namespace imstk
VTKTetrahedralMeshRenderDelegate::VTKTetrahedralMeshRenderDelegate(std::shared_ptr<TetrahedralMesh> tetrahedralMesh) :
m_geometry(tetrahedralMesh)
{
const size_t dim = 4;
// Map vertices
auto mappedVertexArray = vtkSmartPointer<VTKMappedVertexArray>::New();
mappedVertexArray->SetVertexArray(m_geometry->getVerticesPositionsNotConst());
StdVectorOfVec3d& vertices = m_geometry->getVerticesPositionsNotConst();
double* vertData = reinterpret_cast<double*>(vertices.data());
m_mappedVertexArray = vtkSmartPointer<vtkDoubleArray>::New();
m_mappedVertexArray->SetNumberOfComponents(3);
m_mappedVertexArray->SetArray(vertData, vertices.size()*3, 1);
// Create points
auto points = vtkSmartPointer<vtkPoints>::New();
points->SetNumberOfPoints(m_geometry->getNumVertices());
points->SetData(mappedVertexArray);
points->SetData(m_mappedVertexArray);
// Copy cells
auto cells = vtkSmartPointer<vtkCellArray>::New();
vtkIdType cell[dim];
vtkIdType cell[4];
for(const auto &t : m_geometry->getTetrahedraVertices())
{
for(size_t i = 0; i < dim; ++i)
for(size_t i = 0; i < 4; ++i)
{
cell[i] = t[i];
}
cells->InsertNextCell(dim,cell);
cells->InsertNextCell(4,cell);
}
// Create PolyData
// Create Unstructured Grid
auto unstructuredGrid = vtkSmartPointer<vtkUnstructuredGrid>::New();
unstructuredGrid->SetPoints(points);
unstructuredGrid->SetCells(VTK_TETRA, cells);
// Create Source
auto source = vtkSmartPointer<vtkTrivialProducer>::New();
source->SetOutput(unstructuredGrid);
// Set up mapper
// Mapper
auto mapper = vtkSmartPointer<vtkDataSetMapper>::New();
mapper->SetInputConnection(source->GetOutputPort());
mapper->SetInputData(unstructuredGrid);
// Actor
m_actor->SetMapper(mapper);
// Transform
this->updateActorTransform();
}
void
VTKTetrahedralMeshRenderDelegate::update()
{
// Base class update
VTKRenderDelegate::update();
m_mappedVertexArray->Modified(); // TODO: only modify if vertices change
}
std::shared_ptr<Geometry>
VTKTetrahedralMeshRenderDelegate::getGeometry() const
{
......
......@@ -25,13 +25,14 @@
#include <memory>
#include "imstkVTKRenderDelegate.h"
#include "imstkTetrahedralMesh.h"
#include "vtkUnstructuredGrid.h"
class vtkDoubleArray;
namespace imstk
{
class TetrahedralMesh;
///
/// \class TetrahedralMeshRenderDelegate
///
......@@ -50,13 +51,20 @@ public:
///
VTKTetrahedralMeshRenderDelegate(std::shared_ptr<TetrahedralMesh> tetrahedralMesh);
///
/// \brief
///
void update();
///
/// \brief
///
std::shared_ptr<Geometry> getGeometry() const override;
protected:
std::shared_ptr<TetrahedralMesh> m_geometry; ///>
std::shared_ptr<TetrahedralMesh> m_geometry; ///> Geometry to render
vtkSmartPointer<vtkDoubleArray> m_mappedVertexArray; ///> Mapped array of vertices
};
} // imstk
......
......@@ -1207,6 +1207,7 @@ void testPbdCloth()
auto sdk = std::make_shared<imstk::SimulationManager>();
auto scene = sdk->createNewScene("PositionBasedDynamicsTest");
scene->getCamera()->setPosition(6.0, 2.0, 20.0);
scene->getCamera()->setFocalPoint(0, -5, 5);
// a. Construct a sample triangular mesh
......@@ -1215,8 +1216,8 @@ void testPbdCloth()
imstk::StdVectorOfVec3d vertList;
const double width = 10.0;
const double height = 10.0;
const int nRows = 10;
const int nCols = 10;
const int nRows = 11;
const int nCols = 11;
vertList.resize(nRows*nCols);
const double dy = width / (double)(nCols - 1);
const double dx = height / (double)(nRows - 1);
......@@ -1246,28 +1247,46 @@ void testPbdCloth()
surfMesh->setTrianglesVertices(triangles);
// Object & Model
auto deformableObj = std::make_shared<PbdObject>("Cloth");
auto pbdModel = std::make_shared<PbdModel>();
deformableObj->setDynamicalModel(pbdModel);
deformableObj->setVisualGeometry(surfMesh);
deformableObj->setPhysicsGeometry(surfMesh);
deformableObj->initialize(/*Number of constraints*/2,
/*Constraint configuration*/"Distance 0.1",
/*Constraint configuration*/"Dihedral 0.001",
/*Mass*/1.0,
/*Gravity*/"0 -9.8 0",
/*TimeStep*/0.01,
/*FixedPoint*/"1 2 3 4 5 6 7 8 9 10",
/*FixedPoint*/"1 2 3 4 5 6 7 8 9 10 11",
/*NumberOfIterationInConstraintSolver*/5
);
// Solver
auto pbdSolver = std::make_shared<PbdSolver>();
pbdSolver->setPbdObject(deformableObj);
scene->addNonlinearSolver(pbdSolver);
// Light (white)
auto whiteLight = std::make_shared<imstk::Light>("whiteLight");
whiteLight->setPosition(imstk::Vec3d(10, 2, 10));
whiteLight->setFocalPoint(imstk::Vec3d(0, -2, 0));
whiteLight->setPositional();
// Light (red)
auto colorLight = std::make_shared<imstk::Light>("colorLight");
colorLight->setPosition(imstk::Vec3d(5, -3, 5));
colorLight->setFocalPoint(imstk::Vec3d(-5, -5, 0));
colorLight->setColor(imstk::Color::Red);
colorLight->setPositional();
colorLight->setSpotAngle(15);
// Add in scene
scene->addLight(whiteLight);
scene->addLight(colorLight);
scene->addSceneObject(deformableObj);
sdk->setCurrentScene("PositionBasedDynamicsTest");
sdk->startSimulation(true);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment