Options to compute fast normals in MarchingCubes

Adds ComputeFastNormalsForStructured and ComputeFastNormalsForUnstructured
flags to the marching cubes filter.
This commit is contained in:
Sujin Philip 2017-06-19 13:44:36 -04:00
parent 74f1a0bf8b
commit 08fb95d5ee
3 changed files with 174 additions and 2 deletions

@ -68,12 +68,36 @@ public:
VTKM_CONT
bool GetMergeDuplicatePoints() const { return this->Worklet.GetMergeDuplicatePoints(); }
/// Set/Get whether normals should be generated. Off by default. If enabled,
/// the default behaviour is to generate high quality normals for structured
/// datasets, using gradients, and generate fast normals for unstructured
/// datasets based on the result triangle mesh.
///
VTKM_CONT
void SetGenerateNormals(bool on) { this->GenerateNormals = on; }
VTKM_CONT
bool GetGenerateNormals() const { return this->GenerateNormals; }
/// Set/Get whether the fast path should be used for normals computation for
/// structured datasets. Off by default.
VTKM_CONT
void SetComputeFastNormalsForStructured(bool on) { this->ComputeFastNormalsForStructured = on; }
VTKM_CONT
bool GetComputeFastNormalsForStructured() const { return this->ComputeFastNormalsForStructured; }
/// Set/Get whether the fast path should be used for normals computation for
/// unstructured datasets. On by default.
VTKM_CONT
void SetComputeFastNormalsForUnstructured(bool on)
{
this->ComputeFastNormalsForUnstructured = on;
}
VTKM_CONT
bool GetComputeFastNormalsForUnstructured() const
{
return this->ComputeFastNormalsForUnstructured;
}
VTKM_CONT
void SetNormalArrayName(const std::string& name) { this->NormalArrayName = name; }
@ -100,6 +124,8 @@ public:
private:
std::vector<vtkm::Float64> IsoValues;
bool GenerateNormals;
bool ComputeFastNormalsForStructured;
bool ComputeFastNormalsForUnstructured;
std::string NormalArrayName;
vtkm::worklet::MarchingCubes Worklet;
};

@ -20,22 +20,42 @@
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/cont/CellSetSingleType.h>
#include <vtkm/cont/CellSetStructured.h>
#include <vtkm/cont/DynamicArrayHandle.h>
#include <vtkm/cont/DynamicCellSet.h>
#include <vtkm/worklet/DispatcherMapTopology.h>
#include <vtkm/worklet/ScatterCounting.h>
#include <vtkm/worklet/SurfaceNormals.h>
namespace vtkm
{
namespace filter
{
namespace
{
template <typename CellSetList>
bool IsCellSetStructured(const vtkm::cont::DynamicCellSetBase<CellSetList>& cellset)
{
if (cellset.template IsType<vtkm::cont::CellSetStructured<1>>() ||
cellset.template IsType<vtkm::cont::CellSetStructured<2>>() ||
cellset.template IsType<vtkm::cont::CellSetStructured<3>>())
{
return true;
}
return false;
}
} // anonymous namespace
//-----------------------------------------------------------------------------
inline VTKM_CONT MarchingCubes::MarchingCubes()
: vtkm::filter::FilterDataSetWithField<MarchingCubes>()
, IsoValues()
, GenerateNormals(false)
, ComputeFastNormalsForStructured(false)
, ComputeFastNormalsForUnstructured(true)
, NormalArrayName("normals")
, Worklet()
{
@ -138,7 +158,11 @@ inline VTKM_CONT vtkm::filter::ResultDataSet MarchingCubes::DoExecute(
//worklet with the design
//But I think we should get this to compile before we tinker with
//a more efficient api
if (this->GenerateNormals)
bool generateHighQualityNormals = IsCellSetStructured(cells)
? !this->ComputeFastNormalsForStructured
: !this->ComputeFastNormalsForUnstructured;
if (this->GenerateNormals && generateHighQualityNormals)
{
outputCells = this->Worklet.Run(&ivalues[0],
static_cast<vtkm::Id>(ivalues.size()),
@ -162,6 +186,16 @@ inline VTKM_CONT vtkm::filter::ResultDataSet MarchingCubes::DoExecute(
if (this->GenerateNormals)
{
if (!generateHighQualityNormals)
{
Vec3HandleType faceNormals;
vtkm::worklet::FacetedSurfaceNormals faceted;
faceted.Run(outputCells, vertices, faceNormals, device);
vtkm::worklet::SmoothSurfaceNormals smooth;
smooth.Run(outputCells, faceNormals, normals, device);
}
vtkm::cont::Field normalField(this->NormalArrayName, vtkm::cont::Field::ASSOC_POINTS, normals);
output.AddField(normalField);
}

@ -22,8 +22,11 @@
#include <vtkm/cont/ArrayHandleUniformPointCoordinates.h>
#include <vtkm/cont/CellSetSingleType.h>
#include <vtkm/cont/DataSet.h>
#include <vtkm/cont/DataSetBuilderUniform.h>
#include <vtkm/cont/DataSetFieldAdd.h>
#include <vtkm/cont/DynamicArrayHandle.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/CleanGrid.h>
#include <vtkm/filter/MarchingCubes.h>
@ -398,10 +401,119 @@ void TestMarchingCubesCustomPolicy()
"Should have some coordinates");
}
vtkm::cont::DataSet MakeNormalsTestDataSet()
{
vtkm::cont::DataSetBuilderUniform dsb;
vtkm::Id3 dimensions(3, 4, 4);
vtkm::cont::DataSet dataSet = dsb.Create(dimensions);
vtkm::cont::DataSetFieldAdd dsf;
const int nVerts = 48;
vtkm::Float32 vars[nVerts] = { 60.764f, 107.555f, 80.524f, 63.639f, 131.087f, 83.4f,
98.161f, 165.608f, 117.921f, 37.353f, 84.145f, 57.114f,
95.202f, 162.649f, 114.962f, 115.896f, 215.56f, 135.657f,
150.418f, 250.081f, 170.178f, 71.791f, 139.239f, 91.552f,
95.202f, 162.649f, 114.962f, 115.896f, 215.56f, 135.657f,
150.418f, 250.081f, 170.178f, 71.791f, 139.239f, 91.552f,
60.764f, 107.555f, 80.524f, 63.639f, 131.087f, 83.4f,
98.161f, 165.608f, 117.921f, 37.353f, 84.145f, 57.114f };
//Set point and cell scalar
dsf.AddPointField(dataSet, "pointvar", vars, nVerts);
return dataSet;
}
void TestNormals(const vtkm::cont::DataSet& dataset, bool structured)
{
const vtkm::Id numVerts = 16;
const vtkm::Vec<vtkm::FloatDefault, 3> hq[numVerts] = {
{ 0.1510f, 0.6268f, 0.7644f }, { 0.1333f, -0.3974f, 0.9079f },
{ 0.1626f, 0.7642f, 0.6242f }, { 0.3853f, 0.6643f, 0.6405f },
{ -0.1337f, 0.7136f, 0.6876f }, { 0.7705f, -0.4212f, 0.4784f },
{ -0.7360f, -0.4452f, 0.5099f }, { 0.1234f, -0.8871f, 0.4448f },
{ 0.1626f, 0.7642f, -0.6242f }, { 0.3853f, 0.6643f, -0.6405f },
{ -0.1337f, 0.7136f, -0.6876f }, { 0.1510f, 0.6268f, -0.7644f },
{ 0.7705f, -0.4212f, -0.4784f }, { -0.7360f, -0.4452f, -0.5099f },
{ 0.1234f, -0.8871f, -0.4448f }, { 0.1333f, -0.3974f, -0.9079f }
};
const vtkm::Vec<vtkm::FloatDefault, 3> fast[numVerts] = {
{ -0.1351f, 0.4377f, 0.8889f }, { 0.2863f, -0.1721f, 0.9426f },
{ 0.3629f, 0.8155f, 0.4509f }, { 0.8486f, 0.3560f, 0.3914f },
{ -0.8315f, 0.4727f, 0.2917f }, { 0.9395f, -0.2530f, 0.2311f },
{ -0.9105f, -0.0298f, 0.4124f }, { -0.1078f, -0.9585f, 0.2637f },
{ -0.2538f, 0.8534f, -0.4553f }, { 0.8953f, 0.3902f, -0.2149f },
{ -0.8295f, 0.4188f, -0.3694f }, { 0.2434f, 0.4297f, -0.8695f },
{ 0.8951f, -0.1347f, -0.4251f }, { -0.8467f, -0.4258f, -0.3191f },
{ 0.2164f, -0.9401f, -0.2635f }, { -0.1589f, -0.1642f, -0.9735f }
};
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::FloatDefault, 3>> normals;
vtkm::filter::MarchingCubes mc;
mc.SetIsoValue(0, 200);
mc.SetGenerateNormals(true);
// Test default normals generation: high quality for structured, fast for unstructured.
auto expected = structured ? hq : fast;
auto result = mc.Execute(dataset, dataset.GetField("pointvar"));
result.GetDataSet().GetField("normals").GetData().CopyTo(normals);
VTKM_TEST_ASSERT(normals.GetNumberOfValues() == numVerts,
"Wrong number of values in normals field");
for (vtkm::Id i = 0; i < numVerts; ++i)
{
VTKM_TEST_ASSERT(test_equal(normals.GetPortalConstControl().Get(i), expected[i], 0.001),
"Result does not match expected values");
}
// Test the other normals generation method
if (structured)
{
mc.SetComputeFastNormalsForStructured(true);
expected = fast;
}
else
{
mc.SetComputeFastNormalsForUnstructured(false);
expected = hq;
}
result = mc.Execute(dataset, dataset.GetField("pointvar"));
result.GetDataSet().GetField("normals").GetData().CopyTo(normals);
VTKM_TEST_ASSERT(normals.GetNumberOfValues() == numVerts,
"Wrong number of values in normals field");
for (vtkm::Id i = 0; i < numVerts; ++i)
{
VTKM_TEST_ASSERT(test_equal(normals.GetPortalConstControl().Get(i), expected[i], 0.001),
"Result does not match expected values");
}
}
void TestMarchingCubesNormals()
{
std::cout << "Testing MarchingCubes normals generation" << std::endl;
std::cout << "\tStructured dataset\n";
vtkm::cont::DataSet dataset = MakeNormalsTestDataSet();
TestNormals(dataset, true);
std::cout << "\tUnstructured dataset\n";
vtkm::filter::CleanGrid makeUnstructured;
makeUnstructured.SetCompactPointFields(false);
auto result = makeUnstructured.Execute(dataset);
makeUnstructured.MapFieldOntoOutput(result, dataset.GetField("pointvar"));
TestNormals(result.GetDataSet(), false);
}
void TestMarchingCubesFilter()
{
TestMarchingCubesUniformGrid();
TestMarchingCubesCustomPolicy();
TestMarchingCubesNormals();
}
} // anonymous namespace