vtk-m/vtkm/interop/anari/ANARIMapperPoints.cxx
2023-08-30 15:52:48 -05:00

408 lines
14 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt 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.
//============================================================================
// vtk-m
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/interop/anari/ANARIMapperPoints.h>
#include <vtkm/rendering/raytracing/SphereExtractor.h>
#include <vtkm/worklet/WorkletMapField.h>
// anari
#include <anari/anari_cpp/ext/linalg.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
// Worklets ///////////////////////////////////////////////////////////////////
class ExtractPointPositions : public vtkm::worklet::WorkletMapField
{
public:
VTKM_CONT
ExtractPointPositions() = default;
using ControlSignature = void(FieldIn, // [in] index
WholeArrayIn, // [in] point
WholeArrayOut // [out] point
);
using ExecutionSignature = void(InputIndex,
_1, // [in] index
_2, // [in] point
_3 // [out] points
);
template <typename InPointPortalType, typename OutPointPortalType>
VTKM_EXEC void operator()(const vtkm::Id out_idx,
const vtkm::Id in_idx,
const InPointPortalType& points,
OutPointPortalType& outP) const
{
outP.Set(out_idx, static_cast<vtkm::Vec3f_32>(points.Get(in_idx)));
}
};
// Helper functions ///////////////////////////////////////////////////////////
static PointsFieldArrays UnpackFields(FieldSet fields)
{
PointsFieldArrays retval;
using AttributeHandleT = decltype(retval.Field1);
auto makeFieldArray = [](auto field, auto& numComps) -> AttributeHandleT {
if (field.GetNumberOfValues() == 0)
return {};
auto fieldData = field.GetData();
numComps = fieldData.GetNumberOfComponentsFlat();
if (numComps >= 1 && numComps <= 4)
{
vtkm::cont::ArrayHandleRuntimeVec<vtkm::Float32> outData(numComps);
vtkm::cont::ArrayCopyShallowIfPossible(fieldData, outData);
return outData;
}
return {};
};
retval.Field1 = makeFieldArray(fields[0], retval.NumberOfField1Components);
retval.Field2 = makeFieldArray(fields[1], retval.NumberOfField2Components);
retval.Field3 = makeFieldArray(fields[2], retval.NumberOfField3Components);
retval.Field4 = makeFieldArray(fields[3], retval.NumberOfField4Components);
return retval;
}
static PointsArrays UnpackPoints(vtkm::cont::ArrayHandle<vtkm::Id> points,
vtkm::cont::CoordinateSystem coords)
{
PointsArrays retval;
const auto numPoints = points.GetNumberOfValues();
retval.Vertices.Allocate(numPoints);
vtkm::worklet::DispatcherMapField<ExtractPointPositions>().Invoke(
points, coords, retval.Vertices);
return retval;
}
// ANARIMapperPoints definitions //////////////////////////////////////////////
ANARIMapperPoints::ANARIMapperPoints(anari_cpp::Device device,
const ANARIActor& actor,
const std::string& name,
const vtkm::cont::ColorTable& colorTable)
: ANARIMapper(device, actor, name, colorTable)
{
this->Handles = std::make_shared<ANARIMapperPoints::ANARIHandles>();
this->Handles->Device = device;
auto& attributes = this->Handles->Parameters.Vertex.Attribute;
std::fill(attributes.begin(), attributes.end(), nullptr);
anari_cpp::retain(device, device);
}
ANARIMapperPoints::~ANARIMapperPoints()
{
// ensure ANARI handles are released before host memory goes away
this->Handles.reset();
}
void ANARIMapperPoints::SetActor(const ANARIActor& actor)
{
this->ANARIMapper::SetActor(actor);
this->ConstructArrays(true);
this->UpdateMaterial();
}
void ANARIMapperPoints::SetMapFieldAsAttribute(bool enabled)
{
this->ANARIMapper::SetMapFieldAsAttribute(enabled);
this->UpdateGeometry();
this->UpdateMaterial();
}
void ANARIMapperPoints::SetANARIColorMap(anari_cpp::Array1D color,
anari_cpp::Array1D opacity,
bool releaseArrays)
{
this->GetANARISurface();
auto s = this->Handles->Sampler;
if (s)
{
auto d = this->GetDevice();
anari_cpp::setParameter(d, s, "image", color);
anari_cpp::commitParameters(d, s);
}
this->ANARIMapper::SetANARIColorMap(color, opacity, releaseArrays);
}
void ANARIMapperPoints::SetANARIColorMapValueRange(const vtkm::Vec2f_32& valueRange)
{
this->GetANARISurface();
auto s = this->Handles->Sampler;
if (s)
{
auto d = this->GetDevice();
auto scale =
anari_cpp::scaling_matrix(anari_cpp::float3(1.f / (valueRange[1] - valueRange[0])));
auto translation = anari_cpp::translation_matrix(anari_cpp::float3(-valueRange[0], 0, 0));
anari_cpp::setParameter(d, s, "inTransform", anari_cpp::mul(scale, translation));
anari_cpp::commitParameters(d, s);
}
}
anari_cpp::Geometry ANARIMapperPoints::GetANARIGeometry()
{
if (this->Handles->Geometry)
return this->Handles->Geometry;
auto d = this->GetDevice();
this->Handles->Geometry = anari_cpp::newObject<anari_cpp::Geometry>(d, "sphere");
this->ConstructArrays();
this->UpdateGeometry();
return this->Handles->Geometry;
}
anari_cpp::Surface ANARIMapperPoints::GetANARISurface()
{
if (this->Handles->Surface)
return this->Handles->Surface;
auto d = this->GetDevice();
this->Handles->Surface = anari_cpp::newObject<anari_cpp::Surface>(d);
if (!this->Handles->Material)
{
this->Handles->Material = anari_cpp::newObject<anari_cpp::Material>(d, "matte");
anari_cpp::setParameter(d, this->Handles->Material, "name", this->MakeObjectName("material"));
}
auto s = anari_cpp::newObject<anari_cpp::Sampler>(d, "image1D");
this->Handles->Sampler = s;
auto colorArray = anari_cpp::newArray1D(d, ANARI_FLOAT32_VEC4, 3);
auto* colors = anari_cpp::map<vtkm::Vec4f_32>(d, colorArray);
colors[0] = vtkm::Vec4f_32(1.f, 0.f, 0.f, 0.f);
colors[1] = vtkm::Vec4f_32(0.f, 1.f, 0.f, 0.5f);
colors[2] = vtkm::Vec4f_32(0.f, 0.f, 1.f, 1.f);
anari_cpp::unmap(d, colorArray);
anari_cpp::setAndReleaseParameter(d, s, "image", colorArray);
anari_cpp::setParameter(d, s, "filter", "linear");
anari_cpp::setParameter(d, s, "wrapMode", "clampToEdge");
anari_cpp::setParameter(d, s, "name", this->MakeObjectName("colormap"));
anari_cpp::commitParameters(d, s);
this->SetANARIColorMapValueRange(vtkm::Vec2f_32(0.f, 10.f));
this->UpdateMaterial();
anari_cpp::setParameter(d, this->Handles->Surface, "name", this->MakeObjectName("surface"));
anari_cpp::setParameter(d, this->Handles->Surface, "geometry", this->GetANARIGeometry());
anari_cpp::setParameter(d, this->Handles->Surface, "material", this->Handles->Material);
anari_cpp::commitParameters(d, this->Handles->Surface);
return this->Handles->Surface;
}
void ANARIMapperPoints::ConstructArrays(bool regenerate)
{
if (regenerate)
this->Current = false;
if (this->Current)
return;
this->Current = true;
this->Valid = false;
this->Handles->ReleaseArrays();
const auto& actor = this->GetActor();
const auto& coords = actor.GetCoordinateSystem();
if (coords.GetNumberOfPoints() == 0)
{
this->RefreshGroup();
return;
}
vtkm::Bounds coordBounds = coords.GetBounds();
// set a default radius
vtkm::Float64 lx = coordBounds.X.Length();
vtkm::Float64 ly = coordBounds.Y.Length();
vtkm::Float64 lz = coordBounds.Z.Length();
vtkm::Float64 mag = vtkm::Sqrt(lx * lx + ly * ly + lz * lz);
// same as used in vtk ospray
constexpr vtkm::Float64 heuristic = 500.;
auto baseRadius = static_cast<vtkm::Float32>(mag / heuristic);
vtkm::rendering::raytracing::SphereExtractor sphereExtractor;
sphereExtractor.ExtractCoordinates(coords, baseRadius);
auto numPoints = sphereExtractor.GetNumberOfSpheres();
this->Handles->Parameters.NumPrimitives = static_cast<uint32_t>(numPoints);
if (numPoints == 0)
{
this->RefreshGroup();
return;
}
this->PrimaryField = actor.GetPrimaryFieldIndex();
auto pts = sphereExtractor.GetPointIds();
auto arrays = UnpackPoints(pts, coords);
auto fieldArrays = UnpackFields(actor.GetFieldSet());
arrays.Radii = sphereExtractor.GetRadii();
auto* p = (vtkm::Vec3f_32*)arrays.Vertices.GetBuffers()[0].ReadPointerHost(*arrays.Token);
auto* r = (float*)arrays.Radii.GetBuffers()[0].ReadPointerHost(*arrays.Token);
auto d = this->GetDevice();
this->Handles->Parameters.Vertex.Position =
anari_cpp::newArray1D(d, p, NoopANARIDeleter, nullptr, numPoints);
this->Handles->Parameters.Vertex.Radius =
anari_cpp::newArray1D(d, r, NoopANARIDeleter, nullptr, numPoints);
auto createANARIArray = [](auto device, auto fieldArray, auto& token) -> anari_cpp::Array1D {
const auto nv = fieldArray.GetNumberOfValues();
if (nv == 0)
return nullptr;
anari_cpp::DataType type = ANARI_FLOAT32 + fieldArray.GetNumberOfComponents() - 1;
const auto& buffers = fieldArray.GetBuffers();
auto* a = buffers[0].ReadPointerHost(*token);
if (!a && buffers.size() > 1)
a = buffers[1].ReadPointerHost(*token);
return a ? anariNewArray1D(device, a, NoopANARIDeleter, nullptr, type, nv) : nullptr;
};
this->Handles->Parameters.Vertex.Attribute[0] =
createANARIArray(d, fieldArrays.Field1, fieldArrays.Token);
this->Handles->Parameters.Vertex.Attribute[1] =
createANARIArray(d, fieldArrays.Field2, fieldArrays.Token);
this->Handles->Parameters.Vertex.Attribute[2] =
createANARIArray(d, fieldArrays.Field3, fieldArrays.Token);
this->Handles->Parameters.Vertex.Attribute[3] =
createANARIArray(d, fieldArrays.Field4, fieldArrays.Token);
this->UpdateGeometry();
this->UpdateMaterial();
this->Arrays = arrays;
this->FieldArrays = fieldArrays;
this->Valid = true;
this->RefreshGroup();
}
void ANARIMapperPoints::UpdateGeometry()
{
if (!this->Handles->Geometry)
return;
auto d = this->GetDevice();
anari_cpp::unsetParameter(d, this->Handles->Geometry, "vertex.position");
anari_cpp::unsetParameter(d, this->Handles->Geometry, "vertex.radius");
anari_cpp::unsetParameter(d, this->Handles->Geometry, "vertex.attribute0");
anari_cpp::unsetParameter(d, this->Handles->Geometry, "vertex.attribute1");
anari_cpp::unsetParameter(d, this->Handles->Geometry, "vertex.attribute2");
anari_cpp::unsetParameter(d, this->Handles->Geometry, "vertex.attribute3");
anari_cpp::setParameter(d, this->Handles->Geometry, "name", this->MakeObjectName("geometry"));
if (this->Handles->Parameters.Vertex.Position)
{
anari_cpp::setParameter(
d, this->Handles->Geometry, "vertex.position", this->Handles->Parameters.Vertex.Position);
anari_cpp::setParameter(
d, this->Handles->Geometry, "vertex.radius", this->Handles->Parameters.Vertex.Radius);
if (this->GetMapFieldAsAttribute())
{
anari_cpp::setParameter(d,
this->Handles->Geometry,
"vertex.attribute0",
this->Handles->Parameters.Vertex.Attribute[0]);
anari_cpp::setParameter(d,
this->Handles->Geometry,
"vertex.attribute1",
this->Handles->Parameters.Vertex.Attribute[1]);
anari_cpp::setParameter(d,
this->Handles->Geometry,
"vertex.attribute2",
this->Handles->Parameters.Vertex.Attribute[2]);
anari_cpp::setParameter(d,
this->Handles->Geometry,
"vertex.attribute3",
this->Handles->Parameters.Vertex.Attribute[3]);
}
}
anari_cpp::commitParameters(d, this->Handles->Geometry);
}
void ANARIMapperPoints::UpdateMaterial()
{
if (!this->Handles->Material)
return;
auto d = this->GetDevice();
auto s = this->Handles->Sampler;
auto a = this->Handles->Parameters.Vertex.Attribute[PrimaryField];
if (s && a && this->GetMapFieldAsAttribute())
{
anari_cpp::setParameter(d, s, "inAttribute", AnariMaterialInputString(PrimaryField));
anari_cpp::commitParameters(d, s);
anari_cpp::setParameter(d, this->Handles->Material, "color", s);
}
else
anari_cpp::setParameter(d, this->Handles->Material, "color", vtkm::Vec3f_32(1.f));
anari_cpp::commitParameters(d, this->Handles->Material);
}
ANARIMapperPoints::ANARIHandles::~ANARIHandles()
{
this->ReleaseArrays();
anari_cpp::release(this->Device, this->Surface);
anari_cpp::release(this->Device, this->Material);
anari_cpp::release(this->Device, this->Sampler);
anari_cpp::release(this->Device, this->Geometry);
anari_cpp::release(this->Device, this->Device);
}
void ANARIMapperPoints::ANARIHandles::ReleaseArrays()
{
anari_cpp::release(this->Device, this->Parameters.Vertex.Position);
anari_cpp::release(this->Device, this->Parameters.Vertex.Radius);
anari_cpp::release(this->Device, this->Parameters.Vertex.Attribute[0]);
anari_cpp::release(this->Device, this->Parameters.Vertex.Attribute[1]);
anari_cpp::release(this->Device, this->Parameters.Vertex.Attribute[2]);
anari_cpp::release(this->Device, this->Parameters.Vertex.Attribute[3]);
this->Parameters.Vertex.Position = nullptr;
this->Parameters.Vertex.Radius = nullptr;
this->Parameters.Vertex.Attribute[0] = nullptr;
this->Parameters.Vertex.Attribute[1] = nullptr;
this->Parameters.Vertex.Attribute[2] = nullptr;
this->Parameters.Vertex.Attribute[3] = nullptr;
}
} // namespace anari
} // namespace interop
} // namespace vtkm