Add orientation and winding options to SurfaceNormals.

This commit is contained in:
Allison Vacanti 2019-06-26 12:53:46 -04:00
parent 320a5257ad
commit 91e7c4d0b9
4 changed files with 132 additions and 4 deletions

@ -0,0 +1,18 @@
# `SurfaceNormals` filter can now orient normals.
The `OrientNormals` worklet has been added to the `SurfaceNormals` filter, and
is enabled by turning on the `AutoOrientNormals` option. This feature ensures
that all normals generated by the filter will point out of the dataset (or
inward if the `FlipNormals` option is true). In addition,
`SurfaceNormals` now has a `Consistency` option that forces all triangle
windings to be consistent with the cell normal direction (the cell points are
specified in counter-clockwise order around the normal).
This functionality is provided by the following new worklets:
* OrientNormals
* RunOrientCellNormals
* RunOrientPointNormals
* RunOrientPointAndCellNormals
* RunFlipNormals
* TriangleWinding

@ -32,28 +32,64 @@ public:
SurfaceNormals();
/// Set/Get if cell normals should be generated. Default is off.
/// @{
void SetGenerateCellNormals(bool value) { this->GenerateCellNormals = value; }
bool GetGenerateCellNormals() const { return this->GenerateCellNormals; }
/// @}
/// Set/Get if the cell normals should be normalized. Default value is true.
/// The intended use case of this flag is for faster, approximate point
/// normals generation by skipping the normalization of the face normals.
/// Note that when set to false, the result cell normals will not be unit
/// length normals and the point normals will be different.
/// @{
void SetNormalizeCellNormals(bool value) { this->NormalizeCellNormals = value; }
bool GetNormalizeCellNormals() const { return this->NormalizeCellNormals; }
/// @}
/// Set/Get if the point normals should be generated. Default is on.
/// @{
void SetGeneratePointNormals(bool value) { this->GeneratePointNormals = value; }
bool GetGeneratePointNormals() const { return this->GeneratePointNormals; }
/// @}
/// Set/Get the name of the cell normals field. Default is "Normals".
/// @{
void SetCellNormalsName(const std::string& name) { this->CellNormalsName = name; }
const std::string& GetCellNormalsName() const { return this->CellNormalsName; }
/// @}
/// Set/Get the name of the point normals field. Default is "Normals".
/// @{
void SetPointNormalsName(const std::string& name) { this->PointNormalsName = name; }
const std::string& GetPointNormalsName() const { return this->PointNormalsName; }
/// @}
/// If true, the normals will be oriented to face outwards from the surface.
/// This requires a closed manifold surface or the behavior is undefined.
/// This option is expensive but necessary for rendering.
/// To make the normals point inward, set FlipNormals to true.
/// @{
void SetAutoOrientNormals(bool v) { this->AutoOrientNormals = v; }
bool GetAutoOrientNormals() const { return this->AutoOrientNormals; }
/// @}
/// Reverse the normals to point inward when AutoOrientNormals is true.
/// Default is false.
/// @{
void SetFlipNormals(bool v) { this->FlipNormals = v; }
bool GetFlipNormals() const { return this->FlipNormals; }
/// @}
/// Ensure that polygon winding is consistent with normal orientation.
/// Triangles are wound such that their points are counter-clockwise around
/// the generated cell normal. Default is true.
/// @note This currently only affects triangles.
/// @note This is only applied when cell normals are generated.
/// @{
void SetConsistency(bool v) { this->Consistency = v; }
bool GetConsistency() const { return this->Consistency; }
/// @}
template <typename T, typename StorageType, typename DerivedPolicy>
vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& input,
@ -65,6 +101,9 @@ private:
bool GenerateCellNormals;
bool NormalizeCellNormals;
bool GeneratePointNormals;
bool AutoOrientNormals;
bool FlipNormals;
bool Consistency;
std::string CellNormalsName;
std::string PointNormalsName;

@ -8,9 +8,13 @@
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/filter/SurfaceNormals.h>
#include <vtkm/cont/ErrorFilterExecution.h>
#include <vtkm/filter/internal/CreateResult.h>
#include <vtkm/worklet/OrientNormals.h>
#include <vtkm/worklet/SurfaceNormals.h>
#include <vtkm/worklet/TriangleWinding.h>
namespace vtkm
{
@ -58,6 +62,9 @@ inline SurfaceNormals::SurfaceNormals()
: GenerateCellNormals(false)
, NormalizeCellNormals(true)
, GeneratePointNormals(true)
, AutoOrientNormals(false)
, FlipNormals(false)
, Consistency(true)
{
this->SetUseCoordinateSystemAsField(true);
}
@ -76,19 +83,22 @@ inline vtkm::cont::DataSet SurfaceNormals::DoExecute(
throw vtkm::cont::ErrorFilterExecution("No normals selected.");
}
const auto& cellset = input.GetCellSet(this->GetActiveCellSetIndex());
const auto cellset =
vtkm::filter::ApplyPolicyUnstructured(input.GetCellSet(this->GetActiveCellSetIndex()), policy);
const auto& coords = input.GetCoordinateSystem(this->GetActiveCoordinateSystemIndex()).GetData();
vtkm::cont::ArrayHandle<vtkm::Vec3f> faceNormals;
vtkm::worklet::FacetedSurfaceNormals faceted;
faceted.SetNormalize(this->NormalizeCellNormals);
faceted.Run(vtkm::filter::ApplyPolicy(cellset, policy), points, faceNormals);
faceted.Run(cellset, points, faceNormals);
vtkm::cont::DataSet result;
vtkm::cont::ArrayHandle<vtkm::Vec3f> pointNormals;
if (this->GeneratePointNormals)
{
vtkm::cont::ArrayHandle<vtkm::Vec3f> pointNormals;
vtkm::worklet::SmoothSurfaceNormals smooth;
smooth.Run(vtkm::filter::ApplyPolicy(cellset, policy), faceNormals, pointNormals);
smooth.Run(cellset, faceNormals, pointNormals);
result = internal::CreateResult(input,
pointNormals,
@ -111,6 +121,66 @@ inline vtkm::cont::DataSet SurfaceNormals::DoExecute(
cellset.GetName());
}
if (this->AutoOrientNormals)
{
using Orient = vtkm::worklet::OrientNormals;
if (this->GenerateCellNormals && this->GeneratePointNormals)
{
Orient::RunPointAndCellNormals(cellset, coords, pointNormals, faceNormals);
}
else if (this->GenerateCellNormals)
{
Orient::RunCellNormals(cellset, coords, faceNormals);
}
else if (this->GeneratePointNormals)
{
Orient::RunPointNormals(cellset, coords, pointNormals);
}
if (this->FlipNormals)
{
if (this->GenerateCellNormals)
{
Orient::RunFlipNormals(faceNormals);
}
if (this->GeneratePointNormals)
{
Orient::RunFlipNormals(pointNormals);
}
}
}
if (this->Consistency && this->GenerateCellNormals)
{
auto newCells = vtkm::worklet::TriangleWinding::Run(cellset, coords, faceNormals);
// Add the new cells into the result:
vtkm::cont::DataSet newResult;
for (vtkm::Id i = 0; i < result.GetNumberOfCoordinateSystems(); ++i)
{
newResult.AddCoordinateSystem(result.GetCoordinateSystem(i));
}
const vtkm::Id activeCells = this->GetActiveCellSetIndex();
for (vtkm::Id i = 0; i < result.GetNumberOfCellSets(); ++i)
{
if (i != activeCells)
{
newResult.AddCellSet(result.GetCellSet(i));
}
else
{
newResult.AddCellSet(newCells);
}
}
for (vtkm::Id i = 0; i < result.GetNumberOfFields(); ++i)
{
newResult.AddField(result.GetField(i));
}
result = newResult;
}
return result;
}
}

@ -74,6 +74,7 @@ void TestSurfaceNormals()
std::cout << "generate both cell and point normals:\n";
filter.SetGeneratePointNormals(true);
filter.SetAutoOrientNormals(true);
result = filter.Execute(ds);
VTKM_TEST_ASSERT(result.HasField("Normals", vtkm::cont::Field::Association::POINTS),
"Point normals missing.");