initial implementation of ANARI rendering support

This commit is contained in:
Jefferson Amstutz 2023-01-30 20:38:09 -06:00
parent 0c86e3277b
commit db4c5c3b98
38 changed files with 3722 additions and 2 deletions

2
.gitignore vendored

@ -1 +1,3 @@
.DS_Store
*vscode
.anari_deps

@ -65,7 +65,7 @@
- .docker_image
.ubuntu2004_kokkos: &ubuntu2004_kokkos
image: "kitware/vtkm:ci-ubuntu2004_kokkos-20230705"
image: "kitware/vtkm:ci-ubuntu2004_kokkos-20230829"
extends:
- .docker_image

@ -53,6 +53,9 @@ foreach(option IN LISTS options)
elseif(no_rendering STREQUAL option)
set(VTKm_ENABLE_RENDERING "OFF" CACHE STRING "")
elseif(anari STREQUAL option)
set(VTKm_ENABLE_ANARI "ON" CACHE STRING "")
elseif(no_testing STREQUAL option)
set(VTKm_ENABLE_TESTING "OFF" CACHE STRING "")
set(VTKm_ENABLE_TESTING_LIBRARY "OFF" CACHE STRING "")

@ -52,3 +52,25 @@ RUN mkdir -p /opt/kokkos/build && \
cmake -GNinja -DCMAKE_INSTALL_PREFIX=/opt/kokkos -DCMAKE_CXX_FLAGS=-fPIC -DKokkos_ENABLE_SERIAL=ON ../kokkos-$KOKKOS_VERSION &&\
ninja all && \
ninja install
# Build and install ANARI SDK
WORKDIR /opt/anari/src
ARG ANARI_VERSION=0.7.1
RUN curl -L https://github.com/KhronosGroup/ANARI-SDK/archive/refs/tags/v$ANARI_VERSION.tar.gz | tar xzv && \
cmake -GNinja \
-S ANARI-SDK-$ANARI_VERSION \
-B build \
-DBUILD_CTS=OFF \
-DBUILD_EXAMPLES=OFF \
-DBUILD_HELIDE_DEVICE=ON \
-DBUILD_REMOTE_DEVICE=OFF \
-DBUILD_SHARED_LIBS=ON \
-DBUILD_TESTING=OFF \
-DBUILD_VIEWER=OFF \
-DCMAKE_INSTALL_PREFIX=/opt/anari \
-DINSTALL_VIEWER_LIBRARY=OFF && \
cmake --build build && \
cmake --install build && \
rm -rf *
WORKDIR /root

@ -22,7 +22,8 @@ build:ubuntu2004_kokkos:
- .run_automatically
variables:
CMAKE_BUILD_TYPE: RelWithDebInfo
VTKM_SETTINGS: "kokkos+shared+64bit_floats"
CMAKE_PREFIX_PATH: "/opt/anari"
VTKM_SETTINGS: "kokkos+shared+64bit_floats+rendering+anari"
test:ubuntu2004_kokkos:
tags:

@ -72,6 +72,7 @@ set(VTKm_ENABLE_OPENMP "@VTKm_ENABLE_OPENMP@")
set(VTKm_ENABLE_TBB "@VTKm_ENABLE_TBB@")
set(VTKm_ENABLE_LOGGING "@VTKm_ENABLE_LOGGING@")
set(VTKm_ENABLE_RENDERING "@VTKm_ENABLE_RENDERING@")
set(VTKm_ENABLE_ANARI "@VTKm_ENABLE_ANARI@")
set(VTKm_ENABLE_GL_CONTEXT "@VTKm_ENABLE_GL_CONTEXT@")
set(VTKm_ENABLE_OSMESA_CONTEXT "@VTKm_ENABLE_OSMESA_CONTEXT@")
set(VTKm_ENABLE_EGL_CONTEXT "@VTKm_ENABLE_EGL_CONTEXT@")
@ -93,6 +94,7 @@ endif()
include(CMakeFindDependencyMacro)
set(CMAKE_MODULE_PATH_save_vtkm "${CMAKE_MODULE_PATH}")
set(PACKAGE_PREFIX_DIR_save_vtkm "${PACKAGE_PREFIX_DIR}")
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_LIST_DIR}")
if (VTKm_ENABLE_TBB)
@ -103,6 +105,16 @@ if (VTKm_ENABLE_TBB)
endif()
endif()
if (VTKm_ENABLE_ANARI)
find_dependency(anari)
if (NOT anari_FOUND)
set(VTKm_FOUND 0)
list(APPEND VTKm_NOT_FOUND_REASON "ANARI not found: ${anari_NOT_FOUND_MESSAGE}")
endif()
endif()
set(PACKAGE_PREFIX_DIR ${PACKAGE_PREFIX_DIR_save_vtkm})
# Load the library exports, but only if not compiling VTK-m itself
set_and_check(VTKm_CONFIG_DIR "@PACKAGE_VTKm_INSTALL_CONFIG_DIR@")
set(VTKM_FROM_INSTALL_DIR FALSE)

@ -112,6 +112,13 @@ cmake_dependent_option(VTKm_ENABLE_TESTING_LIBRARY "Enable VTKm Testing Library"
OFF "NOT VTKm_ENABLE_TESTING;NOT VTKm_ENABLE_BENCHMARKS" ON)
mark_as_advanced(VTKm_ENABLE_TESTING_LIBRARY)
# The ANARI interop library uses a bit of code in vtkm_rendering, so this option
# currently requires vtkm_rendering to be built. Eventually this dependency
# should go away as vtkm_anari doesn't require applications to use anything from
# vtkm_rendering directly.
cmake_dependent_option(VTKm_ENABLE_ANARI "Enable ANARI interop support"
OFF "VTKm_ENABLE_RENDERING" OFF)
# We may want to make finer controls on whether libraries/modules get built.
# VTK uses the concept of groups for its modules
vtkm_option(VTKm_BUILD_ALL_LIBRARIES
@ -216,6 +223,7 @@ vtkm_module_force_group(Testing
DISABLE_VALUE "DONT_WANT"
)
vtkm_module_force_group(Benchmarking ENABLE_OPTION VTKm_ENABLE_BENCHMARKS)
vtkm_module_force_group(ANARI ENABLE_OPTION VTKm_ENABLE_ANARI)
# The tutorial requires several common filters. This logic might need to
# become more complicated (or less compliated if we decide to always

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:12f5095042fc2bd800d0a556e529103f8e008f10ec112ca0a1acb5b49b283dd7
size 367720

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8d1ff70038dc33f60ea4f002900b6137bc6efb60ca118472b1c4459cc3b67270
size 28689

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:be65f08cf247683224eb8454fc7bfec846b9226fe0c823ac9740c8b06a3b6baf
size 23043

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:17f87d2e7ea22f0a336a782f89e02e2a3dabb778db236045f1e06dc3a4db433f
size 203

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:567eee6acf70a06c58d42afed6c1fa93761ae1a556846c5a9296ef9fd57f98f6
size 306635

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:69d625670cdbf38490b7ba2a72d18c5ac57387ca85d5e34d6008ba67d80395f7
size 152115

@ -0,0 +1,128 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/interop/anari/ANARIActor.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
const char* AnariMaterialInputString(vtkm::IdComponent p)
{
switch (p)
{
case 0:
default:
return "attribute0";
case 1:
return "attribute1";
case 2:
return "attribute2";
case 3:
return "attribute3";
}
return "attribute0";
}
ANARIActor::ANARIActor(const vtkm::cont::UnknownCellSet& cells,
const vtkm::cont::CoordinateSystem& coordinates,
const vtkm::cont::Field& field0,
const vtkm::cont::Field& field1,
const vtkm::cont::Field& field2,
const vtkm::cont::Field& field3)
{
this->Data->Cells = cells;
this->Data->Coordinates = coordinates;
this->Data->Fields[0] = field0;
this->Data->Fields[1] = field1;
this->Data->Fields[2] = field2;
this->Data->Fields[3] = field3;
}
ANARIActor::ANARIActor(const vtkm::cont::UnknownCellSet& cells,
const vtkm::cont::CoordinateSystem& coordinates,
const FieldSet& f)
: ANARIActor(cells, coordinates, f[0], f[1], f[2], f[3])
{
}
ANARIActor::ANARIActor(const vtkm::cont::DataSet& dataset,
const std::string& field0,
const std::string& field1,
const std::string& field2,
const std::string& field3)
{
this->Data->Cells = dataset.GetCellSet();
if (dataset.GetNumberOfCoordinateSystems() > 0)
this->Data->Coordinates = dataset.GetCoordinateSystem();
this->Data->Fields[0] = field0.empty() ? vtkm::cont::Field{} : dataset.GetField(field0);
this->Data->Fields[1] = field1.empty() ? vtkm::cont::Field{} : dataset.GetField(field1);
this->Data->Fields[2] = field2.empty() ? vtkm::cont::Field{} : dataset.GetField(field2);
this->Data->Fields[3] = field3.empty() ? vtkm::cont::Field{} : dataset.GetField(field3);
}
const vtkm::cont::UnknownCellSet& ANARIActor::GetCellSet() const
{
return this->Data->Cells;
}
const vtkm::cont::CoordinateSystem& ANARIActor::GetCoordinateSystem() const
{
return this->Data->Coordinates;
}
const vtkm::cont::Field& ANARIActor::GetField(vtkm::IdComponent idx) const
{
return this->Data->Fields[idx < 0 ? GetPrimaryFieldIndex() : idx];
}
FieldSet ANARIActor::GetFieldSet() const
{
return this->Data->Fields;
}
void ANARIActor::SetPrimaryFieldIndex(vtkm::IdComponent idx)
{
this->Data->PrimaryField = idx;
}
vtkm::IdComponent ANARIActor::GetPrimaryFieldIndex() const
{
return this->Data->PrimaryField;
}
vtkm::cont::DataSet ANARIActor::MakeDataSet(bool includeFields) const
{
vtkm::cont::DataSet dataset;
dataset.SetCellSet(GetCellSet());
dataset.AddCoordinateSystem(GetCoordinateSystem());
if (!includeFields)
return dataset;
auto addField = [&](const vtkm::cont::Field& field) {
if (field.GetNumberOfValues() > 0)
dataset.AddField(field);
};
addField(this->Data->Fields[0]);
addField(this->Data->Fields[1]);
addField(this->Data->Fields[2]);
addField(this->Data->Fields[3]);
return dataset;
}
} // namespace anari
} // namespace interop
} // namespace vtkm

@ -0,0 +1,108 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_interop_anari_ANARIActor_h
#define vtk_m_interop_anari_ANARIActor_h
// vtk-m
#include <vtkm/cont/CoordinateSystem.h>
#include <vtkm/cont/DataSet.h>
#include <vtkm/cont/Field.h>
#include <vtkm/cont/UnknownCellSet.h>
#include <vtkm/interop/anari/VtkmANARITypes.h>
// std
#include <array>
#include <memory>
#include <vtkm/interop/anari/vtkm_anari_export.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
/// \brief Convenience type used to represent all the fields in an `ANARIActor`.
///
using FieldSet = std::array<vtkm::cont::Field, 4>;
/// \brief Returns the appropriate ANARI attribute string based on field index.
///
const char* AnariMaterialInputString(vtkm::IdComponent p);
/// \brief Collects cells, coords, and 0-4 fields for ANARI mappers to consume.
///
/// `ANARIActor` represents a selected set of cells, coordinates, and fields for
/// `ANARIMapper` based mappers to map onto ANARI objects. This class also
/// maintains which field is the "main" field, which almost always is the field
/// which is used to color the geometry or volume.
///
/// Mappers creating geometry will generally add all fields as attribute arrays
/// if possible, letting applications use more than one field as material inputs
/// or data to be color mapped by samplers.
///
struct VTKM_ANARI_EXPORT ANARIActor
{
ANARIActor() = default;
/// @brief Main constructor taking cells, coordinates, and up to 4 fields.
///
ANARIActor(const vtkm::cont::UnknownCellSet& cells,
const vtkm::cont::CoordinateSystem& coordinates,
const vtkm::cont::Field& field0 = {},
const vtkm::cont::Field& field1 = {},
const vtkm::cont::Field& field2 = {},
const vtkm::cont::Field& field3 = {});
/// @brief Convenience constructor when an entire FieldSet already exists.
///
ANARIActor(const vtkm::cont::UnknownCellSet& cells,
const vtkm::cont::CoordinateSystem& coordinates,
const FieldSet& fieldset);
/// @brief Convenience constructor using a dataset + named fields.
///
ANARIActor(const vtkm::cont::DataSet& dataset,
const std::string& field0 = "",
const std::string& field1 = "",
const std::string& field2 = "",
const std::string& field3 = "");
const vtkm::cont::UnknownCellSet& GetCellSet() const;
const vtkm::cont::CoordinateSystem& GetCoordinateSystem() const;
const vtkm::cont::Field& GetField(vtkm::IdComponent idx = -1) const;
FieldSet GetFieldSet() const;
void SetPrimaryFieldIndex(vtkm::IdComponent idx);
vtkm::IdComponent GetPrimaryFieldIndex() const;
/// @brief Utility to reconstitute a DataSet from the items in the actor.
///
vtkm::cont::DataSet MakeDataSet(bool includeFields = false) const;
private:
struct ActorData
{
vtkm::cont::UnknownCellSet Cells;
vtkm::cont::CoordinateSystem Coordinates;
FieldSet Fields;
vtkm::IdComponent PrimaryField{ 0 };
};
std::shared_ptr<ActorData> Data = std::make_shared<ActorData>();
};
} // namespace anari
} // namespace interop
} // namespace vtkm
#endif

@ -0,0 +1,201 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/interop/anari/ANARIMapper.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
ANARIMapper::ANARIMapper(anari_cpp::Device device,
const ANARIActor& actor,
const std::string& name,
const vtkm::cont::ColorTable& colorTable)
: Actor(actor)
, ColorTable(colorTable)
, Name(name)
{
this->Handles = std::make_shared<ANARIHandles>();
this->Handles->Device = device;
anari_cpp::retain(device, device);
}
anari_cpp::Device ANARIMapper::GetDevice() const
{
return this->Handles->Device;
}
const ANARIActor& ANARIMapper::GetActor() const
{
return this->Actor;
}
const char* ANARIMapper::GetName() const
{
return this->Name.c_str();
}
void ANARIMapper::SetActor(const ANARIActor& actor)
{
this->Actor = actor;
}
void ANARIMapper::SetMapFieldAsAttribute(bool enabled)
{
this->MapFieldAsAttribute = enabled;
}
bool ANARIMapper::GetMapFieldAsAttribute() const
{
return this->MapFieldAsAttribute;
}
const vtkm::cont::ColorTable& ANARIMapper::GetColorTable() const
{
return this->ColorTable;
}
void ANARIMapper::SetANARIColorMap(anari_cpp::Array1D color,
anari_cpp::Array1D opacity,
bool releaseArrays)
{
auto d = this->GetDevice();
if (releaseArrays)
{
anari_cpp::release(d, color);
anari_cpp::release(d, opacity);
}
}
void ANARIMapper::SetANARIColorMapValueRange(const vtkm::Vec2f_32&)
{
// no-op
}
void ANARIMapper::SetANARIColorMapOpacityScale(vtkm::Float32)
{
// no-op
}
void ANARIMapper::SetName(const char* name)
{
this->Name = name;
}
void ANARIMapper::SetColorTable(const vtkm::cont::ColorTable& colorTable)
{
this->ColorTable = colorTable;
}
anari_cpp::Geometry ANARIMapper::GetANARIGeometry()
{
return nullptr;
}
anari_cpp::SpatialField ANARIMapper::GetANARISpatialField()
{
return nullptr;
}
anari_cpp::Surface ANARIMapper::GetANARISurface()
{
return nullptr;
}
anari_cpp::Volume ANARIMapper::GetANARIVolume()
{
return nullptr;
}
anari_cpp::Group ANARIMapper::GetANARIGroup()
{
if (!this->Handles->Group)
{
auto d = this->GetDevice();
this->Handles->Group = anari_cpp::newObject<anari_cpp::Group>(d);
this->RefreshGroup();
}
return this->Handles->Group;
}
anari_cpp::Instance ANARIMapper::GetANARIInstance()
{
if (!this->Handles->Instance)
{
auto d = this->GetDevice();
this->Handles->Instance = anari_cpp::newObject<anari_cpp::Instance>(d, "transform");
auto group = this->GetANARIGroup();
anari_cpp::setParameter(d, this->Handles->Instance, "group", group);
anari_cpp::setParameter(d, this->Handles->Instance, "name", MakeObjectName("instance"));
anari_cpp::commitParameters(d, this->Handles->Instance);
}
return this->Handles->Instance;
}
bool ANARIMapper::GroupIsEmpty() const
{
return !this->Valid;
}
std::string ANARIMapper::MakeObjectName(const char* suffix) const
{
std::string name = this->GetName();
name += '.';
name += suffix;
return name;
}
void ANARIMapper::RefreshGroup()
{
if (!this->Handles->Group)
return;
auto d = this->GetDevice();
anari_cpp::unsetParameter(d, this->Handles->Group, "surface");
anari_cpp::unsetParameter(d, this->Handles->Group, "volume");
auto surface = this->GetANARISurface();
auto volume = this->GetANARIVolume();
if (!this->GroupIsEmpty())
{
if (surface)
anari_cpp::setParameterArray1D(d, this->Handles->Group, "surface", &surface, 1);
if (volume)
anari_cpp::setParameterArray1D(d, this->Handles->Group, "volume", &volume, 1);
anari_cpp::setParameter(d, this->Handles->Group, "name", MakeObjectName("group"));
}
anari_cpp::commitParameters(d, this->Handles->Group);
}
vtkm::cont::ColorTable& ANARIMapper::GetColorTable()
{
return this->ColorTable;
}
ANARIMapper::ANARIHandles::~ANARIHandles()
{
anari_cpp::release(this->Device, this->Group);
anari_cpp::release(this->Device, this->Instance);
anari_cpp::release(this->Device, this->Device);
}
} // namespace anari
} // namespace interop
} // namespace vtkm

@ -0,0 +1,139 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_interop_anari_ANARIMapper_h
#define vtk_m_interop_anari_ANARIMapper_h
// vtk-m
#include <vtkm/cont/ColorTable.h>
#include <vtkm/interop/anari/ANARIActor.h>
#include <vtkm/interop/anari/vtkm_anari_export.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
inline void NoopANARIDeleter(const void*, const void*) {}
/// @brief This is the base class used for all ANARI mappers.
///
/// This class implements shared functionality of all ANARI mappers. All ANARI
/// object handle lifetimes are tied to the lifetime of the mapper, including
/// the device. Applications are not intended to ever release handles received
/// from the mapper, unless they manually retain the handle. Additionally,
/// ANARIMappers will update surface or volume objects if changes occur, such as
/// changes to the color map or ANARIActor.
struct VTKM_ANARI_EXPORT ANARIMapper
{
ANARIMapper(anari_cpp::Device device,
const ANARIActor& actor = {},
const std::string& name = "<noname>",
const vtkm::cont::ColorTable& colorTable = vtkm::cont::ColorTable::Preset::Default);
virtual ~ANARIMapper() = default;
anari_cpp::Device GetDevice() const;
const ANARIActor& GetActor() const;
const char* GetName() const;
const vtkm::cont::ColorTable& GetColorTable() const;
void SetName(const char* name);
void SetColorTable(const vtkm::cont::ColorTable& colorTable);
/// @brief Set the current actor on this mapper.
///
/// This sets the actor used to create the geometry. When the actor is changed
/// the mapper will update all the corresponding ANARI objects accordingly.
/// This will not cause new ANARI geometry handles to be made, rather the
/// existing handles will be updated to reflect the new actor's data.
virtual void SetActor(const ANARIActor& actor);
/// @brief Set whether fields from `ANARIActor` should end up as geometry attributes.
///
/// When this is disabled, the mapper will skip creating the data arrays
/// associated with fields for when applications only want the raw geometry.
/// This defaults to being enabled.
virtual void SetMapFieldAsAttribute(bool enabled);
bool GetMapFieldAsAttribute() const;
/// @brief Set color map arrays using raw ANARI array handles.
/// @param color Color array used for color mapping.
/// @param opacity (unused/deprecated, will remove on future ANARI version)
/// @param releaseArrays If true this function will release the hanldes passed in.
virtual void SetANARIColorMap(anari_cpp::Array1D color,
anari_cpp::Array1D opacity,
bool releaseArrays = true);
/// @brief Set the value range (domain) for the color map.
///
virtual void SetANARIColorMapValueRange(const vtkm::Vec2f_32& valueRange);
/// @brief Set a scale factor for opacity (typically used for volumes).
///
virtual void SetANARIColorMapOpacityScale(vtkm::Float32 opacityScale);
/// @brief Get the corresponding ANARIGeometry handle from this mapper.
///
/// NOTE: This handle is not retained, so applications should not release it.
virtual anari_cpp::Geometry GetANARIGeometry();
/// @brief Get the corresponding ANARISpatialField handle from this mapper.
///
/// NOTE: This handle is not retained, so applications should not release it.
virtual anari_cpp::SpatialField GetANARISpatialField();
/// @brief Get the corresponding ANARISurface handle from this mapper.
///
/// NOTE: This handle is not retained, so applications should not release it.
virtual anari_cpp::Surface GetANARISurface();
/// @brief Get the corresponding ANARIVolume handle from this mapper.
///
/// NOTE: This handle is not retained, so applications should not release it.
virtual anari_cpp::Volume GetANARIVolume();
anari_cpp::Group GetANARIGroup();
anari_cpp::Instance GetANARIInstance();
bool GroupIsEmpty() const;
protected:
std::string MakeObjectName(const char* suffix) const;
void RefreshGroup();
vtkm::cont::ColorTable& GetColorTable();
bool Valid{ false };
bool Current{ false };
private:
struct ANARIHandles
{
anari_cpp::Device Device{ nullptr };
anari_cpp::Group Group{ nullptr };
anari_cpp::Instance Instance{ nullptr };
~ANARIHandles();
};
std::shared_ptr<ANARIHandles> Handles;
ANARIActor Actor;
vtkm::cont::ColorTable ColorTable;
std::string Name;
bool MapFieldAsAttribute{ true };
};
} // namespace anari
} // namespace interop
} // namespace vtkm
#endif

@ -0,0 +1,283 @@
//============================================================================
// 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/rendering/raytracing/SphereExtractor.h"
#include <vtkm/VectorAnalysis.h>
#include <vtkm/filter/field_conversion/CellAverage.h>
#include <vtkm/interop/anari/ANARIMapperGlyphs.h>
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
// Worklets ///////////////////////////////////////////////////////////////////
class GeneratePointGlyphs : public vtkm::worklet::WorkletMapField
{
public:
vtkm::Float32 SizeFactor{ 0.f };
bool Offset{ false };
VTKM_CONT
GeneratePointGlyphs(float size = 1.f, bool offset = false)
: SizeFactor(size)
, Offset(offset)
{
}
using ControlSignature = void(FieldIn, WholeArrayIn, WholeArrayOut, WholeArrayOut);
using ExecutionSignature = void(InputIndex, _1, _2, _3, _4);
template <typename InGradientType,
typename InPointPortalType,
typename OutVertexPortalType,
typename OutRadiusPortalType>
VTKM_EXEC void operator()(const vtkm::Id idx,
const InGradientType gradient,
const InPointPortalType& points,
OutVertexPortalType& vertices,
OutRadiusPortalType& radii) const
{
auto ng = vtkm::Normal(static_cast<vtkm::Vec3f_32>(gradient));
auto pt = points.Get(idx);
auto v0 = pt + ng * this->SizeFactor;
auto v1 = pt + ng * -this->SizeFactor;
if (this->Offset)
{
vertices.Set(4 * idx + 0, pt);
vertices.Set(4 * idx + 1, v1);
vertices.Set(4 * idx + 2, v1);
vertices.Set(4 * idx + 3, v1 - (this->SizeFactor * ng));
}
else
{
vertices.Set(4 * idx + 0, v0);
vertices.Set(4 * idx + 1, pt);
vertices.Set(4 * idx + 2, pt);
vertices.Set(4 * idx + 3, v1);
}
radii.Set(4 * idx + 0, this->SizeFactor / 8);
radii.Set(4 * idx + 1, this->SizeFactor / 8);
radii.Set(4 * idx + 2, this->SizeFactor / 4);
radii.Set(4 * idx + 3, 0.f);
}
};
// Helper functions ///////////////////////////////////////////////////////////
static GlyphArrays MakeGlyphs(vtkm::cont::Field gradients,
vtkm::cont::UnknownCellSet cells,
vtkm::cont::CoordinateSystem coords,
float glyphSize,
bool offset)
{
const auto numGlyphs = gradients.GetNumberOfValues();
GlyphArrays retval;
retval.Vertices.Allocate(numGlyphs * 4);
retval.Radii.Allocate(numGlyphs * 4);
GeneratePointGlyphs worklet(glyphSize, offset);
vtkm::worklet::DispatcherMapField<GeneratePointGlyphs> dispatch(worklet);
if (gradients.IsPointField())
dispatch.Invoke(gradients, coords, retval.Vertices, retval.Radii);
else
{
vtkm::cont::DataSet centersInput;
centersInput.AddCoordinateSystem(coords);
centersInput.SetCellSet(cells);
vtkm::filter::field_conversion::CellAverage filter;
filter.SetUseCoordinateSystemAsField(true);
filter.SetOutputFieldName("Centers");
auto centersOutput = filter.Execute(centersInput);
auto resolveField = [&](const auto& concreteField) {
dispatch.Invoke(gradients, concreteField, retval.Vertices, retval.Radii);
};
centersOutput.GetField("Centers")
.GetData()
.CastAndCallForTypesWithFloatFallback<vtkm::TypeListFieldVec3,
vtkm::List<vtkm::cont::StorageTagBasic>>(resolveField);
}
return retval;
}
// ANARIMapperGlyphs definitions //////////////////////////////////////////////
ANARIMapperGlyphs::ANARIMapperGlyphs(anari_cpp::Device device,
const ANARIActor& actor,
const char* name,
const vtkm::cont::ColorTable& colorTable)
: ANARIMapper(device, actor, name, colorTable)
{
this->Handles = std::make_shared<ANARIMapperGlyphs::ANARIHandles>();
this->Handles->Device = device;
anari_cpp::retain(device, device);
}
ANARIMapperGlyphs::~ANARIMapperGlyphs()
{
// ensure ANARI handles are released before host memory goes away
this->Handles.reset();
}
void ANARIMapperGlyphs::SetActor(const ANARIActor& actor)
{
this->ANARIMapper::SetActor(actor);
this->ConstructArrays(true);
}
void ANARIMapperGlyphs::SetOffsetGlyphs(bool enabled)
{
this->Offset = enabled;
}
anari_cpp::Geometry ANARIMapperGlyphs::GetANARIGeometry()
{
if (this->Handles->Geometry)
return this->Handles->Geometry;
auto d = this->GetDevice();
this->Handles->Geometry = anari_cpp::newObject<anari_cpp::Geometry>(d, "cone");
this->ConstructArrays();
this->UpdateGeometry();
return this->Handles->Geometry;
}
anari_cpp::Surface ANARIMapperGlyphs::GetANARISurface()
{
if (this->Handles->Surface)
return this->Handles->Surface;
auto d = this->GetDevice();
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"));
}
anari_cpp::commitParameters(d, this->Handles->Material);
this->Handles->Surface = anari_cpp::newObject<anari_cpp::Surface>(d);
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 ANARIMapperGlyphs::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();
const auto& cells = actor.GetCellSet();
const auto& field = actor.GetField();
auto numGlyphs = field.GetNumberOfValues();
if (numGlyphs == 0)
{
this->RefreshGroup();
return;
}
vtkm::Bounds coordBounds = coords.GetBounds();
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);
constexpr vtkm::Float64 heuristic = 300.;
auto glyphSize = static_cast<vtkm::Float32>(mag / heuristic);
auto arrays = MakeGlyphs(field, cells, coords, glyphSize, Offset);
auto* v = (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, v, NoopANARIDeleter, nullptr, arrays.Vertices.GetNumberOfValues());
this->Handles->Parameters.Vertex.Radius =
anari_cpp::newArray1D(d, r, NoopANARIDeleter, nullptr, arrays.Radii.GetNumberOfValues());
this->Handles->Parameters.NumPrimitives = numGlyphs;
this->UpdateGeometry();
this->Arrays = arrays;
this->Valid = true;
this->RefreshGroup();
}
void ANARIMapperGlyphs::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::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);
anari_cpp::setParameter(d, this->Handles->Geometry, "caps", "both");
}
anari_cpp::commitParameters(d, this->Handles->Geometry);
}
ANARIMapperGlyphs::ANARIHandles::~ANARIHandles()
{
this->ReleaseArrays();
anari_cpp::release(this->Device, this->Surface);
anari_cpp::release(this->Device, this->Material);
anari_cpp::release(this->Device, this->Geometry);
anari_cpp::release(this->Device, this->Device);
}
void ANARIMapperGlyphs::ANARIHandles::ReleaseArrays()
{
anari_cpp::release(this->Device, this->Parameters.Vertex.Position);
anari_cpp::release(this->Device, this->Parameters.Vertex.Radius);
this->Parameters.Vertex.Position = nullptr;
this->Parameters.Vertex.Radius = nullptr;
}
} // namespace anari
} // namespace interop
} // namespace vtkm

@ -0,0 +1,118 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_interop_anari_ANARIMapperGlyphs_h
#define vtk_m_interop_anari_ANARIMapperGlyphs_h
#include <vtkm/interop/anari/ANARIMapper.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
/// @brief Raw ANARI arrays and parameter values set on the `ANARIGeometry`.
///
struct GlyphsParameters
{
struct VertexData
{
anari_cpp::Array1D Position{ nullptr };
anari_cpp::Array1D Radius{ nullptr };
} Vertex{};
unsigned int NumPrimitives{ 0 };
};
/// @brief VTK-m data arrays underlying the `ANARIArray` handles created by the mapper.
///
struct GlyphArrays
{
vtkm::cont::ArrayHandle<vtkm::Vec3f_32> Vertices;
vtkm::cont::ArrayHandle<vtkm::Float32> Radii;
std::shared_ptr<vtkm::cont::Token> Token{ new vtkm::cont::Token };
};
/// @brief Mapper which turns vector data into arrow glyphs.
///
/// This mapper creates ANARI `cone` geometry for the primary field in the
/// provided `ANARIActor`.
struct VTKM_ANARI_EXPORT ANARIMapperGlyphs : public ANARIMapper
{
/// @brief Constructor
///
ANARIMapperGlyphs(
anari_cpp::Device device,
const ANARIActor& actor = {},
const char* name = "<glyphs>",
const vtkm::cont::ColorTable& colorTable = vtkm::cont::ColorTable::Preset::Default);
/// @brief Destructor
///
~ANARIMapperGlyphs() override;
/// @brief Set the current actor on this mapper.
///
/// This sets the actor used to create the geometry. When the actor is changed
/// the mapper will update all the corresponding ANARI objects accordingly.
/// This will not cause new ANARI geometry handles to be made, rather the
/// existing handles will be updated to reflect the new actor's data.
void SetActor(const ANARIActor& actor) override;
/// @brief Offset the glyph in the direction of the vector at each point.
///
/// This will cause the mapper to offset the glyph, making the arrow appear to
/// be coming out of the point instead of going through it. This is useful for
/// visualizing things like surface normals on a mesh.
void SetOffsetGlyphs(bool enabled);
/// @brief Get the corresponding ANARIGeometry handle from this mapper.
///
/// NOTE: This handle is not retained, so applications should not release it.
anari_cpp::Geometry GetANARIGeometry() override;
/// @brief Get the corresponding ANARISurface handle from this mapper.
///
/// NOTE: This handle is not retained, so applications should not release it.
anari_cpp::Surface GetANARISurface() override;
private:
/// @brief Do the work to construct the basic ANARI arrays for the ANARIGeometry.
/// @param regenerate Force the position/radius arrays are regenerated.
///
void ConstructArrays(bool regenerate = false);
/// @brief Update ANARIGeometry object with the latest data from the actor.
void UpdateGeometry();
/// @brief Container of all relevant ANARI scene object handles.
struct ANARIHandles
{
anari_cpp::Device Device{ nullptr };
anari_cpp::Geometry Geometry{ nullptr };
anari_cpp::Material Material{ nullptr };
anari_cpp::Surface Surface{ nullptr };
GlyphsParameters Parameters;
~ANARIHandles();
void ReleaseArrays();
};
std::shared_ptr<ANARIHandles> Handles;
bool Offset{ false };
GlyphArrays Arrays;
};
} // namespace anari
} // namespace interop
} // namespace vtkm
#endif

@ -0,0 +1,407 @@
//============================================================================
// 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

@ -0,0 +1,137 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_interop_anari_ANARIMapperPoints_h
#define vtk_m_interop_anari_ANARIMapperPoints_h
#include <vtkm/cont/ArrayHandleRuntimeVec.h>
#include <vtkm/interop/anari/ANARIMapper.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
/// @brief Raw ANARI arrays and parameter values set on the `ANARIGeometry`.
///
struct PointsParameters
{
struct VertexData
{
anari_cpp::Array1D Position{ nullptr };
anari_cpp::Array1D Radius{ nullptr };
std::array<anari_cpp::Array1D, 4> Attribute;
} Vertex{};
unsigned int NumPrimitives{ 0 };
};
/// @brief VTK-m data arrays underlying the `ANARIArray` handles created by the mapper.
///
struct PointsArrays
{
vtkm::cont::ArrayHandle<vtkm::Vec3f_32> Vertices;
vtkm::cont::ArrayHandle<vtkm::Float32> Radii;
std::shared_ptr<vtkm::cont::Token> Token{ new vtkm::cont::Token };
};
/// @brief VTK-m data arrays underlying the `ANARIArray` handles created by the mapper for field attributes.
///
struct PointsFieldArrays
{
vtkm::cont::ArrayHandleRuntimeVec<vtkm::Float32> Field1;
int NumberOfField1Components{ 1 };
vtkm::cont::ArrayHandleRuntimeVec<vtkm::Float32> Field2;
int NumberOfField2Components{ 1 };
vtkm::cont::ArrayHandleRuntimeVec<vtkm::Float32> Field3;
int NumberOfField3Components{ 1 };
vtkm::cont::ArrayHandleRuntimeVec<vtkm::Float32> Field4;
int NumberOfField4Components{ 1 };
std::shared_ptr<vtkm::cont::Token> Token{ new vtkm::cont::Token };
};
/// @brief Mapper which turns each point into ANARI `sphere` geometry.
///
/// NOTE: This mapper will color map values that are 1/2/3/4 component Float32
/// fields, otherwise they will be ignored.
struct VTKM_ANARI_EXPORT ANARIMapperPoints : public ANARIMapper
{
/// @brief Constructor
///
ANARIMapperPoints(
anari_cpp::Device device,
const ANARIActor& actor = {},
const std::string& name = "<points>",
const vtkm::cont::ColorTable& colorTable = vtkm::cont::ColorTable::Preset::Default);
/// @brief Destructor
///
~ANARIMapperPoints() override;
/// @brief Set the current actor on this mapper.
///
/// See `ANARIMapper` for more detail.
void SetActor(const ANARIActor& actor) override;
/// @brief Set whether fields from `ANARIActor` should end up as geometry attributes.
///
/// See `ANARIMapper` for more detail.
void SetMapFieldAsAttribute(bool enabled) override;
/// @brief Set color map arrays using raw ANARI array handles.
///
/// See `ANARIMapper` for more detail.
void SetANARIColorMap(anari_cpp::Array1D color,
anari_cpp::Array1D opacity,
bool releaseArrays = true) override;
/// @brief Set the value range (domain) for the color map.
///
void SetANARIColorMapValueRange(const vtkm::Vec2f_32& valueRange) override;
anari_cpp::Geometry GetANARIGeometry() override;
anari_cpp::Surface GetANARISurface() override;
private:
/// @brief Do the work to construct the basic ANARI arrays for the ANARIGeometry.
/// @param regenerate Force the position/radius arrays are regenerated.
///
void ConstructArrays(bool regenerate = false);
/// @brief Update ANARIGeometry object with the latest data from the actor.
void UpdateGeometry();
/// @brief Update ANARIMaterial object with the latest data from the actor.
void UpdateMaterial();
/// @brief Container of all relevant ANARI scene object handles.
struct ANARIHandles
{
anari_cpp::Device Device{ nullptr };
anari_cpp::Geometry Geometry{ nullptr };
anari_cpp::Sampler Sampler{ nullptr };
anari_cpp::Material Material{ nullptr };
anari_cpp::Surface Surface{ nullptr };
PointsParameters Parameters;
~ANARIHandles();
void ReleaseArrays();
};
std::shared_ptr<ANARIHandles> Handles;
vtkm::IdComponent PrimaryField{ 0 };
PointsArrays Arrays;
PointsFieldArrays FieldArrays;
};
} // namespace anari
} // namespace interop
} // namespace vtkm
#endif

@ -0,0 +1,611 @@
//============================================================================
// 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/filter/vector_analysis/SurfaceNormals.h>
#include <vtkm/interop/anari/ANARIMapperTriangles.h>
#include <vtkm/rendering/raytracing/TriangleExtractor.h>
#include <vtkm/worklet/WorkletMapField.h>
// std
#include <numeric>
// anari
#include <anari/anari_cpp/ext/linalg.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
// Worklets ///////////////////////////////////////////////////////////////////
class ExtractTriangleFields : public vtkm::worklet::WorkletMapField
{
public:
bool PopulateField1{ false };
bool PopulateField2{ false };
bool PopulateField3{ false };
bool PopulateField4{ false };
vtkm::Range Field1Range;
vtkm::Range Field2Range;
vtkm::Range Field3Range;
vtkm::Range Field4Range;
VTKM_CONT
ExtractTriangleFields(bool emptyField1,
bool emptyField2,
bool emptyField3,
bool emptyField4,
vtkm::Range field1Range,
vtkm::Range field2Range,
vtkm::Range field3Range,
vtkm::Range field4Range)
: PopulateField1(!emptyField1)
, PopulateField2(!emptyField2)
, PopulateField3(!emptyField3)
, PopulateField4(!emptyField4)
, Field1Range(field1Range)
, Field2Range(field2Range)
, Field3Range(field3Range)
, Field4Range(field4Range)
{
}
using ControlSignature = void(FieldIn,
WholeArrayIn, // [in] field1
WholeArrayIn, // [in] field2
WholeArrayIn, // [in] field3
WholeArrayIn, // [in] field4
WholeArrayOut, // [out] field1
WholeArrayOut, // [out] field2
WholeArrayOut, // [out] field3
WholeArrayOut // [out] field4
);
using ExecutionSignature = void(InputIndex,
_1, // [in] indices
_2, // [in] field1
_3, // [in] field2
_4, // [in] field3
_5, // [in] field4
_6, // [out] field1
_7, // [out] field2
_8, // [out] field3
_9 // [out] field4
);
template <typename FieldPortalType, typename OutFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id idx,
const vtkm::Id4 indices,
const FieldPortalType& field1,
const FieldPortalType& field2,
const FieldPortalType& field3,
const FieldPortalType& field4,
OutFieldPortalType& outF1,
OutFieldPortalType& outF2,
OutFieldPortalType& outF3,
OutFieldPortalType& outF4) const
{
const auto i0 = indices[1];
const auto i1 = indices[2];
const auto i2 = indices[3];
if (this->PopulateField1)
{
outF1.Set(3 * idx + 0, static_cast<vtkm::Float32>(field1.Get(i0)));
outF1.Set(3 * idx + 1, static_cast<vtkm::Float32>(field1.Get(i1)));
outF1.Set(3 * idx + 2, static_cast<vtkm::Float32>(field1.Get(i2)));
}
if (this->PopulateField2)
{
outF2.Set(3 * idx + 0, static_cast<vtkm::Float32>(field2.Get(i0)));
outF2.Set(3 * idx + 1, static_cast<vtkm::Float32>(field2.Get(i1)));
outF2.Set(3 * idx + 2, static_cast<vtkm::Float32>(field2.Get(i2)));
}
if (this->PopulateField3)
{
outF3.Set(3 * idx + 0, static_cast<vtkm::Float32>(field3.Get(i0)));
outF3.Set(3 * idx + 1, static_cast<vtkm::Float32>(field3.Get(i1)));
outF3.Set(3 * idx + 2, static_cast<vtkm::Float32>(field3.Get(i2)));
}
if (this->PopulateField4)
{
outF4.Set(3 * idx + 0, static_cast<vtkm::Float32>(field4.Get(i0)));
outF4.Set(3 * idx + 1, static_cast<vtkm::Float32>(field4.Get(i1)));
outF4.Set(3 * idx + 2, static_cast<vtkm::Float32>(field4.Get(i2)));
}
}
};
class ExtractTriangleVerticesAndNormals : public vtkm::worklet::WorkletMapField
{
public:
bool ExtractNormals{ false };
VTKM_CONT
ExtractTriangleVerticesAndNormals(bool withNormals)
: ExtractNormals(withNormals)
{
}
using ControlSignature = void(FieldIn,
WholeArrayIn, // [in] points
WholeArrayIn, // [in] normals
WholeArrayOut, // [out] points
WholeArrayOut // [out] normals
);
using ExecutionSignature = void(InputIndex,
_1, // [in] indices
_2, // [in] points
_3, // [in] normals
_4, // [out] points
_5 // [out] normals
);
template <typename PointPortalType,
typename NormalPortalType,
typename OutPointsPortalType,
typename OutNormalsPortalType>
VTKM_EXEC void operator()(const vtkm::Id idx,
const vtkm::Id4 indices,
const PointPortalType& points,
const NormalPortalType& normals,
OutPointsPortalType& outP,
OutNormalsPortalType& outN) const
{
const auto i0 = indices[1];
const auto i1 = indices[2];
const auto i2 = indices[3];
outP.Set(3 * idx + 0, static_cast<vtkm::Vec3f_32>(points.Get(i0)));
outP.Set(3 * idx + 1, static_cast<vtkm::Vec3f_32>(points.Get(i1)));
outP.Set(3 * idx + 2, static_cast<vtkm::Vec3f_32>(points.Get(i2)));
if (this->ExtractNormals)
{
outN.Set(3 * idx + 0, static_cast<vtkm::Vec3f_32>(normals.Get(i0)));
outN.Set(3 * idx + 1, static_cast<vtkm::Vec3f_32>(normals.Get(i1)));
outN.Set(3 * idx + 2, static_cast<vtkm::Vec3f_32>(normals.Get(i2)));
}
}
};
// Helper functions ///////////////////////////////////////////////////////////
static TriangleFieldArrays UnpackFields(vtkm::cont::ArrayHandle<vtkm::Id4> tris,
FieldSet fields,
vtkm::Range range)
{
TriangleFieldArrays retval;
const auto numTris = tris.GetNumberOfValues();
using AttributeHandleT = decltype(retval.Field1);
auto isFieldEmpty = [](const vtkm::cont::Field& f) -> bool {
return f.GetNumberOfValues() == 0 || f.GetData().GetNumberOfComponentsFlat() != 1 ||
!f.GetData().CanConvert<AttributeHandleT>();
};
const bool emptyField1 = isFieldEmpty(fields[0]);
const bool emptyField2 = isFieldEmpty(fields[1]);
const bool emptyField3 = isFieldEmpty(fields[2]);
const bool emptyField4 = isFieldEmpty(fields[3]);
auto field1 =
emptyField1 ? AttributeHandleT{} : fields[0].GetData().AsArrayHandle<AttributeHandleT>();
auto field2 =
emptyField2 ? AttributeHandleT{} : fields[1].GetData().AsArrayHandle<AttributeHandleT>();
auto field3 =
emptyField3 ? AttributeHandleT{} : fields[2].GetData().AsArrayHandle<AttributeHandleT>();
auto field4 =
emptyField4 ? AttributeHandleT{} : fields[3].GetData().AsArrayHandle<AttributeHandleT>();
vtkm::Range field1Range = range;
vtkm::Range field2Range = range;
vtkm::Range field3Range = range;
vtkm::Range field4Range = range;
if (!emptyField1)
{
retval.Field1.Allocate(numTris * 3);
}
if (!emptyField2)
{
retval.Field2.Allocate(numTris * 3);
}
if (!emptyField3)
{
retval.Field3.Allocate(numTris * 3);
}
if (!emptyField4)
{
retval.Field4.Allocate(numTris * 3);
}
ExtractTriangleFields fieldsWorklet(emptyField1,
emptyField2,
emptyField3,
emptyField4,
field1Range,
field2Range,
field3Range,
field4Range);
vtkm::worklet::DispatcherMapField<ExtractTriangleFields>(fieldsWorklet)
.Invoke(tris,
field1,
field2,
field3,
field4,
retval.Field1,
retval.Field2,
retval.Field3,
retval.Field4);
return retval;
}
static TriangleArrays UnpackTriangles(vtkm::cont::ArrayHandle<vtkm::Id4> tris,
vtkm::cont::CoordinateSystem coords,
vtkm::cont::ArrayHandle<vtkm::Vec3f_32> normals)
{
TriangleArrays retval;
const auto numTris = tris.GetNumberOfValues();
bool extractNormals = normals.GetNumberOfValues() != 0;
retval.Vertices.Allocate(numTris * 3);
if (extractNormals)
retval.Normals.Allocate(numTris * 3);
ExtractTriangleVerticesAndNormals worklet(extractNormals);
vtkm::worklet::DispatcherMapField<ExtractTriangleVerticesAndNormals>(worklet).Invoke(
tris, coords, normals, retval.Vertices, retval.Normals);
return retval;
}
// ANARIMapperTriangles definitions ///////////////////////////////////////////
ANARIMapperTriangles::ANARIMapperTriangles(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<ANARIMapperTriangles::ANARIHandles>();
this->Handles->Device = device;
auto& attributes = this->Handles->Parameters.Vertex.Attribute;
std::fill(attributes.begin(), attributes.end(), nullptr);
anari_cpp::retain(device, device);
}
ANARIMapperTriangles::~ANARIMapperTriangles()
{
// ensure ANARI handles are released before host memory goes away
this->Handles.reset();
}
void ANARIMapperTriangles::SetActor(const ANARIActor& actor)
{
this->ANARIMapper::SetActor(actor);
this->ConstructArrays(true);
this->UpdateMaterial();
}
void ANARIMapperTriangles::SetMapFieldAsAttribute(bool enabled)
{
this->ANARIMapper::SetMapFieldAsAttribute(enabled);
this->UpdateGeometry();
this->UpdateMaterial();
}
void ANARIMapperTriangles::SetANARIColorMap(anari_cpp::Array1D color,
anari_cpp::Array1D opacity,
bool releaseArrays)
{
this->GetANARISurface();
auto d = this->GetDevice();
auto s = this->Handles->Sampler;
anari_cpp::setParameter(d, s, "image", color);
anari_cpp::commitParameters(d, s);
this->ANARIMapper::SetANARIColorMap(color, opacity, releaseArrays);
}
void ANARIMapperTriangles::SetANARIColorMapValueRange(const vtkm::Vec2f_32& valueRange)
{
this->GetANARISurface();
auto s = this->Handles->Sampler;
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);
}
void ANARIMapperTriangles::SetCalculateNormals(bool enabled)
{
this->CalculateNormals = enabled;
}
anari_cpp::Geometry ANARIMapperTriangles::GetANARIGeometry()
{
if (this->Handles->Geometry)
return this->Handles->Geometry;
auto d = this->GetDevice();
this->Handles->Geometry = anari_cpp::newObject<anari_cpp::Geometry>(d, "triangle");
this->ConstructArrays();
this->UpdateGeometry();
return this->Handles->Geometry;
}
anari_cpp::Surface ANARIMapperTriangles::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;
}
bool ANARIMapperTriangles::NeedToGenerateData() const
{
const bool haveNormals = this->Handles->Parameters.Vertex.Normal != nullptr;
const bool needNormals = this->CalculateNormals && !haveNormals;
return !this->Current || needNormals;
}
void ANARIMapperTriangles::ConstructArrays(bool regenerate)
{
if (regenerate)
this->Current = false;
if (!regenerate && !this->NeedToGenerateData())
return;
this->Current = true;
this->Valid = false;
this->Handles->ReleaseArrays();
const auto& actor = this->GetActor();
const auto& cells = actor.GetCellSet();
if (cells.GetNumberOfCells() == 0)
{
this->RefreshGroup();
return;
}
vtkm::rendering::raytracing::TriangleExtractor triExtractor;
triExtractor.ExtractCells(cells);
if (triExtractor.GetNumberOfTriangles() == 0)
{
this->RefreshGroup();
return;
}
vtkm::cont::ArrayHandle<vtkm::Vec3f_32> inNormals;
if (this->CalculateNormals)
{
vtkm::filter::vector_analysis::SurfaceNormals normalsFilter;
normalsFilter.SetOutputFieldName("Normals");
auto dataset = normalsFilter.Execute(actor.MakeDataSet());
auto field = dataset.GetField("Normals");
auto fieldArray = field.GetData();
vtkm::cont::ArrayCopyShallowIfPossible(fieldArray, inNormals);
}
auto tris = triExtractor.GetTriangles();
auto arrays = UnpackTriangles(tris, actor.GetCoordinateSystem(), inNormals);
auto fieldArrays = UnpackFields(tris, actor.GetFieldSet(), this->GetColorTable().GetRange());
this->PrimaryField = actor.GetPrimaryFieldIndex();
auto numVerts = arrays.Vertices.GetNumberOfValues();
auto* v = (vtkm::Vec3f_32*)arrays.Vertices.GetBuffers()[0].ReadPointerHost(*arrays.Token);
auto* n = (vtkm::Vec3f_32*)arrays.Normals.GetBuffers()[0].ReadPointerHost(*arrays.Token);
auto d = this->GetDevice();
this->Handles->Parameters.NumPrimitives = numVerts / 3;
this->Handles->Parameters.Vertex.Position =
anari_cpp::newArray1D(d, v, NoopANARIDeleter, nullptr, numVerts);
if (fieldArrays.Field1.GetNumberOfValues() != 0)
{
auto* a = (float*)fieldArrays.Field1.GetBuffers()[0].ReadPointerHost(*fieldArrays.Token);
this->Handles->Parameters.Vertex.Attribute[0] =
anari_cpp::newArray1D(d, a, NoopANARIDeleter, nullptr, numVerts);
}
if (fieldArrays.Field2.GetNumberOfValues() != 0)
{
auto* a = (float*)fieldArrays.Field2.GetBuffers()[0].ReadPointerHost(*fieldArrays.Token);
this->Handles->Parameters.Vertex.Attribute[1] =
anari_cpp::newArray1D(d, a, NoopANARIDeleter, nullptr, numVerts);
}
if (fieldArrays.Field3.GetNumberOfValues() != 0)
{
auto* a = (float*)fieldArrays.Field3.GetBuffers()[0].ReadPointerHost(*fieldArrays.Token);
this->Handles->Parameters.Vertex.Attribute[2] =
anari_cpp::newArray1D(d, a, NoopANARIDeleter, nullptr, numVerts);
}
if (fieldArrays.Field4.GetNumberOfValues() != 0)
{
auto* a = (float*)fieldArrays.Field4.GetBuffers()[0].ReadPointerHost(*fieldArrays.Token);
this->Handles->Parameters.Vertex.Attribute[3] =
anari_cpp::newArray1D(d, a, NoopANARIDeleter, nullptr, numVerts);
}
if (this->CalculateNormals)
{
this->Handles->Parameters.Vertex.Normal =
anari_cpp::newArray1D(d, n, NoopANARIDeleter, nullptr, arrays.Normals.GetNumberOfValues());
}
// NOTE: usd device requires indices, but shouldn't
{
auto indexArray =
anari_cpp::newArray1D(d, ANARI_UINT32_VEC3, this->Handles->Parameters.NumPrimitives);
auto* begin = anari_cpp::map<unsigned int>(d, indexArray);
auto* end = begin + numVerts;
std::iota(begin, end, 0);
anari_cpp::unmap(d, indexArray);
this->Handles->Parameters.Primitive.Index = indexArray;
}
this->UpdateGeometry();
this->UpdateMaterial();
this->Arrays = arrays;
this->FieldArrays = fieldArrays;
this->Valid = true;
this->RefreshGroup();
}
void ANARIMapperTriangles::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.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::unsetParameter(d, this->Handles->Geometry, "vertex.normal");
anari_cpp::unsetParameter(d, this->Handles->Geometry, "primitive.index");
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);
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]);
}
if (CalculateNormals)
{
anari_cpp::setParameter(
d, this->Handles->Geometry, "vertex.normal", this->Handles->Parameters.Vertex.Normal);
}
anari_cpp::setParameter(
d, this->Handles->Geometry, "primitive.index", this->Handles->Parameters.Primitive.Index);
}
anari_cpp::commitParameters(d, this->Handles->Geometry);
}
void ANARIMapperTriangles::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);
}
ANARIMapperTriangles::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 ANARIMapperTriangles::ANARIHandles::ReleaseArrays()
{
anari_cpp::release(this->Device, this->Parameters.Vertex.Position);
anari_cpp::release(this->Device, this->Parameters.Vertex.Normal);
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]);
anari_cpp::release(this->Device, this->Parameters.Primitive.Index);
this->Parameters.Vertex.Position = nullptr;
this->Parameters.Vertex.Normal = 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;
this->Parameters.Primitive.Index = nullptr;
}
} // namespace anari
} // namespace interop
} // namespace vtkm

@ -0,0 +1,145 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_interop_anari_ANARIMapperTriangles_h
#define vtk_m_interop_anari_ANARIMapperTriangles_h
#include <vtkm/interop/anari/ANARIMapper.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
/// @brief Raw ANARI arrays and parameter values set on the `ANARIGeometry`.
///
struct TrianglesParameters
{
struct VertexData
{
anari_cpp::Array1D Position{ nullptr };
anari_cpp::Array1D Normal{ nullptr };
std::array<anari_cpp::Array1D, 4> Attribute;
} Vertex{};
struct PrimitiveData
{
anari_cpp::Array1D Index{ nullptr };
} Primitive{};
unsigned int NumPrimitives{ 0 };
};
/// @brief VTK-m data arrays underlying the `ANARIArray` handles created by the mapper.
///
struct TriangleArrays
{
vtkm::cont::ArrayHandle<vtkm::Vec3f_32> Vertices;
vtkm::cont::ArrayHandle<vtkm::Vec3f_32> Normals;
std::shared_ptr<vtkm::cont::Token> Token{ new vtkm::cont::Token };
};
/// @brief VTK-m data arrays underlying the `ANARIArray` handles created by the mapper for field attributes.
///
struct TriangleFieldArrays
{
vtkm::cont::ArrayHandle<vtkm::Float32> Field1;
vtkm::cont::ArrayHandle<vtkm::Float32> Field2;
vtkm::cont::ArrayHandle<vtkm::Float32> Field3;
vtkm::cont::ArrayHandle<vtkm::Float32> Field4;
std::shared_ptr<vtkm::cont::Token> Token{ new vtkm::cont::Token };
};
/// @brief Mapper which triangulates cells into ANARI `triangle` geometry.
///
/// NOTE: This mapper will only color map values that are 1-component Float32
/// fields, otherwise they will be ignored. This restriction will allow 2-4
/// component fields in the future.
struct VTKM_ANARI_EXPORT ANARIMapperTriangles : public ANARIMapper
{
/// @brief Constructor
///
ANARIMapperTriangles(
anari_cpp::Device device,
const ANARIActor& actor = {},
const std::string& name = "<triangles>",
const vtkm::cont::ColorTable& colorTable = vtkm::cont::ColorTable::Preset::Default);
/// @brief Destructor
///
~ANARIMapperTriangles() override;
/// @brief Set the current actor on this mapper.
///
/// See `ANARIMapper` for more detail.
void SetActor(const ANARIActor& actor) override;
/// @brief Set whether fields from `ANARIActor` should end up as geometry attributes.
///
/// See `ANARIMapper` for more detail.
void SetMapFieldAsAttribute(bool enabled) override;
/// @brief Set color map arrays using raw ANARI array handles.
///
/// See `ANARIMapper` for more detail.
void SetANARIColorMap(anari_cpp::Array1D color,
anari_cpp::Array1D opacity,
bool releaseArrays = true) override;
/// @brief Set the value range (domain) for the color map.
///
void SetANARIColorMapValueRange(const vtkm::Vec2f_32& valueRange) override;
/// @brief Set whether `vertex.normal` data should also be calculated when triangulating geometry.
///
void SetCalculateNormals(bool enabled);
anari_cpp::Geometry GetANARIGeometry() override;
anari_cpp::Surface GetANARISurface() override;
private:
bool NeedToGenerateData() const;
/// @brief Do the work to construct the basic ANARI arrays for the ANARIGeometry.
/// @param regenerate Force the position/radius arrays are regenerated.
///
void ConstructArrays(bool regenerate = false);
/// @brief Update ANARIGeometry object with the latest data from the actor.
void UpdateGeometry();
/// @brief Update ANARIMaterial object with the latest data from the actor.
void UpdateMaterial();
/// @brief Container of all relevant ANARI scene object handles.
struct ANARIHandles
{
anari_cpp::Device Device{ nullptr };
anari_cpp::Geometry Geometry{ nullptr };
anari_cpp::Sampler Sampler{ nullptr };
anari_cpp::Material Material{ nullptr };
anari_cpp::Surface Surface{ nullptr };
TrianglesParameters Parameters;
~ANARIHandles();
void ReleaseArrays();
};
std::shared_ptr<ANARIHandles> Handles;
bool CalculateNormals{ false };
vtkm::IdComponent PrimaryField{ 0 };
TriangleArrays Arrays;
TriangleFieldArrays FieldArrays;
};
} // namespace anari
} // namespace interop
} // namespace vtkm
#endif

@ -0,0 +1,211 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/interop/anari/ANARIMapperVolume.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
ANARIMapperVolume::ANARIMapperVolume(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<ANARIMapperVolume::ANARIHandles>();
this->Handles->Device = device;
anari_cpp::retain(device, device);
}
ANARIMapperVolume::~ANARIMapperVolume()
{
// ensure ANARI handles are released before host memory goes away
this->Handles.reset();
}
void ANARIMapperVolume::SetActor(const ANARIActor& actor)
{
this->ANARIMapper::SetActor(actor);
this->ConstructArrays(true);
}
void ANARIMapperVolume::SetANARIColorMap(anari_cpp::Array1D color,
anari_cpp::Array1D opacity,
bool releaseArrays)
{
auto d = this->GetDevice();
auto v = this->GetANARIVolume();
anari_cpp::setParameter(d, v, "color", color);
anari_cpp::setParameter(d, v, "opacity", opacity);
anari_cpp::commitParameters(d, v);
this->ANARIMapper::SetANARIColorMap(color, opacity, releaseArrays);
}
void ANARIMapperVolume::SetANARIColorMapValueRange(const vtkm::Vec2f_32& valueRange)
{
auto d = this->GetDevice();
auto v = this->GetANARIVolume();
anari_cpp::setParameter(d, v, "valueRange", ANARI_FLOAT32_BOX1, &valueRange);
anari_cpp::commitParameters(d, v);
}
void ANARIMapperVolume::SetANARIColorMapOpacityScale(vtkm::Float32 opacityScale)
{
auto d = this->GetDevice();
auto v = this->GetANARIVolume();
anari_cpp::setParameter(d, v, "densityScale", opacityScale);
anari_cpp::commitParameters(d, v);
}
anari_cpp::SpatialField ANARIMapperVolume::GetANARISpatialField()
{
if (this->Handles->SpatialField)
return this->Handles->SpatialField;
this->Handles->SpatialField =
anari_cpp::newObject<anari_cpp::SpatialField>(this->GetDevice(), "structuredRegular");
this->ConstructArrays();
this->UpdateSpatialField();
return this->Handles->SpatialField;
}
anari_cpp::Volume ANARIMapperVolume::GetANARIVolume()
{
if (this->Handles->Volume)
return this->Handles->Volume;
auto d = this->GetDevice();
this->Handles->Volume = anari_cpp::newObject<anari_cpp::Volume>(d, "transferFunction1D");
auto colorArray = anari_cpp::newArray1D(d, ANARI_FLOAT32_VEC3, 3);
auto* colors = anari_cpp::map<vtkm::Vec3f_32>(d, colorArray);
colors[0] = vtkm::Vec3f_32(1.f, 0.f, 0.f);
colors[1] = vtkm::Vec3f_32(0.f, 1.f, 0.f);
colors[2] = vtkm::Vec3f_32(0.f, 0.f, 1.f);
anari_cpp::unmap(d, colorArray);
auto opacityArray = anari_cpp::newArray1D(d, ANARI_FLOAT32, 2);
auto* opacities = anari_cpp::map<float>(d, opacityArray);
opacities[0] = 0.f;
opacities[1] = 1.f;
anari_cpp::unmap(d, opacityArray);
anari_cpp::setAndReleaseParameter(d, this->Handles->Volume, "color", colorArray);
anari_cpp::setAndReleaseParameter(d, this->Handles->Volume, "opacity", opacityArray);
anari_cpp::setParameter(d, this->Handles->Volume, "field", this->GetANARISpatialField());
anari_cpp::setParameter(d, this->Handles->Volume, "name", this->MakeObjectName("volume"));
anari_cpp::commitParameters(d, this->Handles->Volume);
SetANARIColorMapValueRange(vtkm::Vec2f_32(0.f, 10.f));
return this->Handles->Volume;
}
void ANARIMapperVolume::ConstructArrays(bool regenerate)
{
if (regenerate)
this->Current = false;
if (this->Current)
return;
this->Current = true;
this->Valid = false;
const auto& actor = this->GetActor();
const auto& coords = actor.GetCoordinateSystem();
const auto& cells = actor.GetCellSet();
const auto& fieldArray = actor.GetField().GetData();
const bool isStructured = cells.IsType<vtkm::cont::CellSetStructured<3>>();
const bool isFloat = fieldArray.IsType<vtkm::cont::ArrayHandle<vtkm::Float32>>();
this->Handles->ReleaseArrays();
if (isStructured && isFloat)
{
auto structuredCells = cells.AsCellSet<vtkm::cont::CellSetStructured<3>>();
auto pdims = structuredCells.GetPointDimensions();
VolumeArrays arrays;
auto d = this->GetDevice();
arrays.Data = fieldArray.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Float32>>();
auto* ptr = (float*)arrays.Data.GetBuffers()[0].ReadPointerHost(*arrays.Token);
auto bounds = coords.GetBounds();
vtkm::Vec3f_32 bLower(bounds.X.Min, bounds.Y.Min, bounds.Z.Min);
vtkm::Vec3f_32 bUpper(bounds.X.Max, bounds.Y.Max, bounds.Z.Max);
vtkm::Vec3f_32 size = bUpper - bLower;
vtkm::Vec3ui_32 dims(pdims[0], pdims[1], pdims[2]);
auto spacing = size / (vtkm::Vec3f_32(dims) - 1.f);
std::memcpy(this->Handles->Parameters.Dims, &dims, sizeof(dims));
std::memcpy(this->Handles->Parameters.Origin, &bLower, sizeof(bLower));
std::memcpy(this->Handles->Parameters.Spacing, &spacing, sizeof(spacing));
this->Handles->Parameters.Data =
anari_cpp::newArray3D(d, ptr, NoopANARIDeleter, nullptr, dims[0], dims[1], dims[2]);
this->Arrays = arrays;
this->Valid = true;
}
this->UpdateSpatialField();
this->RefreshGroup();
}
void ANARIMapperVolume::UpdateSpatialField()
{
auto d = this->GetDevice();
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "origin");
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "spacing");
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "data");
anari_cpp::setParameter(
d, this->Handles->SpatialField, "name", this->MakeObjectName("spatialField"));
if (this->Handles->Parameters.Data)
{
anari_cpp::setParameter(
d, this->Handles->SpatialField, "origin", this->Handles->Parameters.Origin);
anari_cpp::setParameter(
d, this->Handles->SpatialField, "spacing", this->Handles->Parameters.Spacing);
anari_cpp::setParameter(d, this->Handles->SpatialField, "data", this->Handles->Parameters.Data);
}
anari_cpp::commitParameters(d, this->Handles->SpatialField);
}
ANARIMapperVolume::ANARIHandles::~ANARIHandles()
{
this->ReleaseArrays();
anari_cpp::release(this->Device, this->Volume);
anari_cpp::release(this->Device, this->SpatialField);
anari_cpp::release(this->Device, this->Device);
}
void ANARIMapperVolume::ANARIHandles::ReleaseArrays()
{
anari_cpp::release(this->Device, this->Parameters.Data);
this->Parameters.Data = nullptr;
}
} // namespace anari
} // namespace interop
} // namespace vtkm

@ -0,0 +1,108 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_interop_anari_ANARIMapperVolume_h
#define vtk_m_interop_anari_ANARIMapperVolume_h
#include <vtkm/interop/anari/ANARIMapper.h>
namespace vtkm
{
namespace interop
{
namespace anari
{
/// @brief Raw ANARI arrays and parameter values set on the `ANARISpatialField`.
///
struct VolumeParameters
{
anari_cpp::Array3D Data{ nullptr };
int Dims[3];
float Origin[3];
float Spacing[3];
};
/// @brief VTK-m data arrays underlying the `ANARIArray` handles created by the mapper.
///
struct VolumeArrays
{
vtkm::cont::ArrayHandle<vtkm::Float32> Data;
std::shared_ptr<vtkm::cont::Token> Token{ new vtkm::cont::Token };
};
/// @brief Mapper which turns structured volumes into a single ANARI `transferFunction1D` volume.
///
/// NOTE: This currently only supports Float32 scalar fields. In the future this
/// mapper will also support Uint8, Uint16, and Float64 scalar fields.
struct VTKM_ANARI_EXPORT ANARIMapperVolume : public ANARIMapper
{
/// @brief Constructor
///
ANARIMapperVolume(
anari_cpp::Device device,
const ANARIActor& actor = {},
const std::string& name = "<volume>",
const vtkm::cont::ColorTable& colorTable = vtkm::cont::ColorTable::Preset::Default);
/// @brief Destructor
///
~ANARIMapperVolume() override;
/// @brief Set the current actor on this mapper.
///
/// See `ANARIMapper` for more detail.
void SetActor(const ANARIActor& actor) override;
/// @brief Set color map arrays using raw ANARI array handles.
///
/// See `ANARIMapper` for more detail.
void SetANARIColorMap(anari_cpp::Array1D color,
anari_cpp::Array1D opacity,
bool releaseArrays = true) override;
/// @brief Set the value range (domain) for the color map.
///
void SetANARIColorMapValueRange(const vtkm::Vec2f_32& valueRange) override;
/// @brief Set a scale factor for opacity.
///
void SetANARIColorMapOpacityScale(vtkm::Float32 opacityScale) override;
anari_cpp::SpatialField GetANARISpatialField() override;
anari_cpp::Volume GetANARIVolume() override;
private:
/// @brief Do the work to construct the basic ANARI arrays for the ANARIGeometry.
/// @param regenerate Force the position/radius arrays are regenerated.
///
void ConstructArrays(bool regenerate = false);
/// @brief Update ANARISpatialField object with the latest data from the actor.
void UpdateSpatialField();
/// @brief Container of all relevant ANARI scene object handles.
struct ANARIHandles
{
anari_cpp::Device Device{ nullptr };
anari_cpp::SpatialField SpatialField{ nullptr };
anari_cpp::Volume Volume{ nullptr };
VolumeParameters Parameters;
~ANARIHandles();
void ReleaseArrays();
};
std::shared_ptr<ANARIHandles> Handles;
VolumeArrays Arrays;
};
} // namespace anari
} // namespace interop
} // namespace vtkm
#endif

@ -0,0 +1,155 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/interop/anari/ANARIScene.h>
// std
#include <algorithm>
namespace vtkm
{
namespace interop
{
namespace anari
{
ANARIScene::ANARIScene(anari_cpp::Device device)
: Device(device)
{
anari_cpp::retain(this->Device, this->Device);
}
ANARIScene::~ANARIScene()
{
anari_cpp::release(this->Device, this->World);
anari_cpp::release(this->Device, this->Device);
}
vtkm::IdComponent ANARIScene::GetNumberOfMappers() const
{
return static_cast<vtkm::IdComponent>(this->Mappers.size());
}
bool ANARIScene::HasMapperWithName(const char* _name) const
{
std::string name = _name;
auto itr = std::find_if(this->Mappers.begin(), this->Mappers.end(), [&](auto& m) {
return m.Mapper->GetName() == name;
});
return itr != this->Mappers.end();
}
vtkm::IdComponent ANARIScene::GetMapperIndexByName(const char* _name)
{
std::string name = _name;
auto itr = std::find_if(this->Mappers.begin(), this->Mappers.end(), [&](auto& m) {
return m.Mapper->GetName() == name;
});
return static_cast<vtkm::IdComponent>(std::distance(this->Mappers.begin(), itr));
}
ANARIMapper& ANARIScene::GetMapper(vtkm::IdComponent id)
{
return *this->Mappers[id].Mapper;
}
ANARIMapper& ANARIScene::GetMapper(const char* _name)
{
std::string name = _name;
auto itr = std::find_if(this->Mappers.begin(), this->Mappers.end(), [&](auto& m) {
return m.Mapper->GetName() == name;
});
return *itr->Mapper;
}
bool ANARIScene::GetMapperVisible(vtkm::IdComponent id) const
{
return this->Mappers[id].Show;
}
void ANARIScene::SetMapperVisible(vtkm::IdComponent id, bool shown)
{
auto& m = this->Mappers[id];
if (m.Show != shown)
{
m.Show = shown;
this->UpdateWorld();
}
}
void ANARIScene::RemoveMapper(vtkm::IdComponent id)
{
this->Mappers.erase(this->Mappers.begin() + id);
this->UpdateWorld();
}
void ANARIScene::RemoveMapper(const char* name)
{
std::string n = name;
this->Mappers.erase(std::remove_if(this->Mappers.begin(),
this->Mappers.end(),
[&](auto& m) { return m.Mapper->GetName() == n; }),
this->Mappers.end());
this->UpdateWorld();
}
void ANARIScene::RemoveAllMappers()
{
this->Mappers.clear();
this->UpdateWorld();
}
anari_cpp::Device ANARIScene::GetDevice() const
{
return this->Device;
}
anari_cpp::World ANARIScene::GetANARIWorld()
{
if (!this->World)
{
auto d = this->GetDevice();
this->World = anari_cpp::newObject<anari_cpp::World>(d);
anari_cpp::setParameter(d, this->World, "name", "scene");
this->UpdateWorld();
}
return this->World;
}
void ANARIScene::UpdateWorld()
{
if (!this->World)
return; // nobody has asked for the world yet, so don't actually do anything
auto d = this->GetDevice();
std::vector<anari_cpp::Instance> instances;
for (auto& m : this->Mappers)
{
auto i = m.Mapper->GetANARIInstance();
if (i && m.Show)
instances.push_back(i);
}
if (!instances.empty())
{
anari_cpp::setAndReleaseParameter(
d, this->World, "instance", anari_cpp::newArray1D(d, instances.data(), instances.size()));
}
else
anari_cpp::unsetParameter(d, this->World, "instance");
anari_cpp::commitParameters(d, this->World);
}
} // namespace anari
} // namespace interop
} // namespace vtkm

@ -0,0 +1,176 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_interop_anari_ANARIScene_h
#define vtk_m_interop_anari_ANARIScene_h
#include <vtkm/interop/anari/ANARIMapper.h>
// std
#include <string>
#include <type_traits>
namespace vtkm
{
namespace interop
{
namespace anari
{
/// @brief Object which manages a collection of mappers representing a single scene.
///
/// This object is a container of named mappers which will automatically keep
/// an `ANARIWorld` up to date which contains any `ANARISurface` or
/// `ANARIVolume` objects coming from the contained mappers. While applications
/// are free to do this work themselves, it is very convenient and useful to
/// let `ANARIScene` do the work of keeping an `ANARIWorld` up to date for you.
///
/// Mappers in `ANARIScene` can also be selectively hidden for quick scene
/// updates. A hidden mapper's geometry/volume are just skipped when updating
/// the list of object handles in the world.
///
/// NOTE: This object will not create any lights in the scene, so the
/// `ANARIWorld` used by the application is expected to have application-managed
/// `ANARILight` objects added to it when desired.
///
/// NOTE: Unlike `ANARIMapper` and `ANARIActor`, `ANARIScene` is not C++
/// copyable or movable.
struct VTKM_ANARI_EXPORT ANARIScene
{
/// @brief Constructor
///
ANARIScene(anari_cpp::Device device);
/// @brief Destructor
///
~ANARIScene();
ANARIScene(const ANARIScene&) = delete;
ANARIScene(ANARIScene&&) = delete;
ANARIScene& operator=(const ANARIScene&) = delete;
ANARIScene& operator=(ANARIScene&&) = delete;
/// @brief Add a mapper to the scene.
/// @tparam ANARIMapperType Any subclass of `ANARIMapper`.
///
/// NOTE: This will replace any mapper that has the same name.
template <typename ANARIMapperType>
ANARIMapperType& AddMapper(const ANARIMapperType& mapper, bool visible = true);
/// @brief Add a mapper to the scene.
/// @tparam ANARIMapperType Any subclass of `ANARIMapper`.
///
/// NOTE: Replace the i'th mapper with a new instance.
/// NOTE: It is undefined behavior to use this to put 2 or more mappers in the
/// scene with the same name.
template <typename ANARIMapperType>
void ReplaceMapper(const ANARIMapperType& newMapper, vtkm::IdComponent id, bool visible);
/// @brief Get number of mappers in this scene.
///
vtkm::IdComponent GetNumberOfMappers() const;
/// @brief Ask whether a mapper has the passed in name or not.
///
bool HasMapperWithName(const char* name) const;
/// @brief Get the index to the mapper with the given name.
///
vtkm::IdComponent GetMapperIndexByName(const char* name);
/// @brief Get the associated mapper by index.
///
ANARIMapper& GetMapper(vtkm::IdComponent id);
/// @brief Get the associated mapper by name.
///
ANARIMapper& GetMapper(const char* name);
/// @brief Get the associated mapper by name.
///
bool GetMapperVisible(vtkm::IdComponent id) const;
void SetMapperVisible(vtkm::IdComponent id, bool shown);
/// @brief Remove mapper by index.
///
void RemoveMapper(vtkm::IdComponent id);
/// @brief Remove mapper by name.
///
void RemoveMapper(const char* name);
/// @brief Clear out this scene of all mappers.
void RemoveAllMappers();
/// @brief Get the `ANARIDevice` handle this scene is talking to.
///
/// NOTE: This handle is not retained, so applications should not release it.
anari_cpp::Device GetDevice() const;
/// @brief Get the `ANARIWorld` handle this scene is working on.
///
/// NOTE: This handle is not retained, so applications should not release it.
anari_cpp::World GetANARIWorld();
private:
void UpdateWorld();
anari_cpp::Device Device{ nullptr };
anari_cpp::World World{ nullptr };
struct SceneMapper
{
std::unique_ptr<ANARIMapper> Mapper;
bool Show{ true };
};
std::vector<SceneMapper> Mappers;
};
// Inlined definitions ////////////////////////////////////////////////////////
template <typename ANARIMapperType>
inline ANARIMapperType& ANARIScene::AddMapper(const ANARIMapperType& mapper, bool visible)
{
static_assert(std::is_base_of<ANARIMapper, ANARIMapperType>::value,
"Only ANARIMapper types can be added to ANARIScene");
auto* name = mapper.GetName();
if (HasMapperWithName(name))
{
auto idx = GetMapperIndexByName(name);
ReplaceMapper(mapper, idx, visible);
return (ANARIMapperType&)GetMapper(idx);
}
else
{
this->Mappers.push_back({ std::make_unique<ANARIMapperType>(mapper), visible });
UpdateWorld();
return (ANARIMapperType&)GetMapper(GetNumberOfMappers() - 1);
}
}
template <typename ANARIMapperType>
inline void ANARIScene::ReplaceMapper(const ANARIMapperType& newMapper,
vtkm::IdComponent id,
bool visible)
{
static_assert(std::is_base_of<ANARIMapper, ANARIMapperType>::value,
"Only ANARIMapper types can be added to ANARIScene");
const bool wasVisible = GetMapperVisible(id);
Mappers[id] = { std::make_unique<ANARIMapperType>(newMapper), visible };
if (wasVisible || visible)
UpdateWorld();
}
} // namespace anari
} // namespace interop
} // namespace vtkm
#endif

@ -0,0 +1,47 @@
##============================================================================
## 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.
##============================================================================
find_package(anari 0.7.0 REQUIRED)
set(headers
ANARIActor.h
ANARIMapper.h
ANARIMapperGlyphs.h
ANARIMapperPoints.h
ANARIMapperTriangles.h
ANARIMapperVolume.h
ANARIScene.h
VtkmANARITypes.h
)
set(sources
ANARIActor.cxx
ANARIMapper.cxx
ANARIScene.cxx
VtkmANARITypes.cxx
)
set(device_sources
ANARIMapperGlyphs.cxx
ANARIMapperPoints.cxx
ANARIMapperTriangles.cxx
ANARIMapperVolume.cxx
../../rendering/raytracing/SphereExtractor.cxx
)
vtkm_library(
NAME vtkm_anari
SOURCES ${sources}
HEADERS ${headers}
DEVICE_SOURCES ${device_sources}
)
target_link_libraries(vtkm_anari PUBLIC anari::anari)

@ -0,0 +1,26 @@
//============================================================================
// 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.
//============================================================================
#include "VtkmANARITypes.h"
namespace anari
{
ANARI_TYPEFOR_DEFINITION(vtkm::Vec2f_32);
ANARI_TYPEFOR_DEFINITION(vtkm::Vec3f_32);
ANARI_TYPEFOR_DEFINITION(vtkm::Vec4f_32);
ANARI_TYPEFOR_DEFINITION(vtkm::Vec2i_32);
ANARI_TYPEFOR_DEFINITION(vtkm::Vec3i_32);
ANARI_TYPEFOR_DEFINITION(vtkm::Vec4i_32);
ANARI_TYPEFOR_DEFINITION(vtkm::Vec2ui_32);
ANARI_TYPEFOR_DEFINITION(vtkm::Vec3ui_32);
ANARI_TYPEFOR_DEFINITION(vtkm::Vec4ui_32);
} // namespace anari

@ -0,0 +1,43 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_interop_anari_VtkmANARITypes_h
#define vtk_m_interop_anari_VtkmANARITypes_h
// vtk-m
#include <vtkm/Types.h>
// anari
#include <anari/anari_cpp.hpp>
#if ANARI_SDK_VERSION_MINOR <= 3
#include <anari/type_utility.h>
#endif
namespace anari_cpp = ::anari;
namespace anari
{
/// These declarations let ANARI C++ bindings infer the correct `ANARIDataType`
/// enum value from VTK-m's C++ math types. This header should be included
/// before any code which needs this type inference to function.
ANARI_TYPEFOR_SPECIALIZATION(vtkm::Vec2f_32, ANARI_FLOAT32_VEC2);
ANARI_TYPEFOR_SPECIALIZATION(vtkm::Vec3f_32, ANARI_FLOAT32_VEC3);
ANARI_TYPEFOR_SPECIALIZATION(vtkm::Vec4f_32, ANARI_FLOAT32_VEC4);
ANARI_TYPEFOR_SPECIALIZATION(vtkm::Vec2i_32, ANARI_INT32_VEC2);
ANARI_TYPEFOR_SPECIALIZATION(vtkm::Vec3i_32, ANARI_INT32_VEC3);
ANARI_TYPEFOR_SPECIALIZATION(vtkm::Vec4i_32, ANARI_INT32_VEC4);
ANARI_TYPEFOR_SPECIALIZATION(vtkm::Vec2ui_32, ANARI_UINT32_VEC2);
ANARI_TYPEFOR_SPECIALIZATION(vtkm::Vec3ui_32, ANARI_UINT32_VEC3);
ANARI_TYPEFOR_SPECIALIZATION(vtkm::Vec4ui_32, ANARI_UINT32_VEC4);
} // namespace anari
#endif

@ -0,0 +1,149 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_interop_anari_testing_ANARITestCommon_h
#define vtk_m_interop_anari_testing_ANARITestCommon_h
// vtk-m
#include <vtkm/cont/DataSetBuilderUniform.h>
#include <vtkm/interop/anari/ANARIMapper.h>
#include <vtkm/rendering/testing/Testing.h>
#include <vtkm/testing/Testing.h>
namespace
{
static void StatusFunc(const void* userData,
ANARIDevice /*device*/,
ANARIObject source,
ANARIDataType /*sourceType*/,
ANARIStatusSeverity severity,
ANARIStatusCode /*code*/,
const char* message)
{
bool verbose = *(bool*)userData;
if (!verbose)
return;
if (severity == ANARI_SEVERITY_FATAL_ERROR)
{
fprintf(stderr, "[FATAL][%p] %s\n", source, message);
}
else if (severity == ANARI_SEVERITY_ERROR)
{
fprintf(stderr, "[ERROR][%p] %s\n", source, message);
}
else if (severity == ANARI_SEVERITY_WARNING)
{
fprintf(stderr, "[WARN ][%p] %s\n", source, message);
}
else if (severity == ANARI_SEVERITY_PERFORMANCE_WARNING)
{
fprintf(stderr, "[PERF ][%p] %s\n", source, message);
}
else if (severity == ANARI_SEVERITY_INFO)
{
fprintf(stderr, "[INFO ][%p] %s\n", source, message);
}
else if (severity == ANARI_SEVERITY_DEBUG)
{
fprintf(stderr, "[DEBUG][%p] %s\n", source, message);
}
}
static void setColorMap(anari_cpp::Device d, vtkm::interop::anari::ANARIMapper& mapper)
{
auto colorArray = anari_cpp::newArray1D(d, ANARI_FLOAT32_VEC3, 3);
auto* colors = anari_cpp::map<vtkm::Vec3f_32>(d, colorArray);
colors[0] = vtkm::Vec3f_32(0.f, 0.f, 1.f);
colors[1] = vtkm::Vec3f_32(0.f, 1.f, 0.f);
colors[2] = vtkm::Vec3f_32(1.f, 0.f, 0.f);
anari_cpp::unmap(d, colorArray);
auto opacityArray = anari_cpp::newArray1D(d, ANARI_FLOAT32, 2);
auto* opacities = anari_cpp::map<float>(d, opacityArray);
opacities[0] = 0.f;
opacities[1] = 1.f;
anari_cpp::unmap(d, opacityArray);
mapper.SetANARIColorMap(colorArray, opacityArray, true);
mapper.SetANARIColorMapValueRange(vtkm::Vec2f_32(0.f, 10.f));
mapper.SetANARIColorMapOpacityScale(0.5f);
}
static anari_cpp::Device loadANARIDevice()
{
vtkm::testing::FloatingPointExceptionTrapDisable();
auto* libraryName = std::getenv("VTKM_TEST_ANARI_LIBRARY");
static bool verbose = std::getenv("VTKM_TEST_ANARI_VERBOSE") != nullptr;
auto lib = anari_cpp::loadLibrary(libraryName ? libraryName : "helide", StatusFunc, &verbose);
auto d = anari_cpp::newDevice(lib, "default");
anari_cpp::unloadLibrary(lib);
return d;
}
static void renderTestANARIImage(anari_cpp::Device d,
anari_cpp::World w,
vtkm::Vec3f_32 cam_pos,
vtkm::Vec3f_32 cam_dir,
vtkm::Vec3f_32 cam_up,
const std::string& imgName,
vtkm::Vec2ui_32 imgSize = vtkm::Vec2ui_32(1024, 768))
{
auto renderer = anari_cpp::newObject<anari_cpp::Renderer>(d, "default");
anari_cpp::setParameter(d, renderer, "background", vtkm::Vec4f_32(0.3f, 0.3f, 0.3f, 1.f));
anari_cpp::setParameter(d, renderer, "pixelSamples", 64);
anari_cpp::commitParameters(d, renderer);
auto camera = anari_cpp::newObject<anari_cpp::Camera>(d, "perspective");
anari_cpp::setParameter(d, camera, "aspect", imgSize[0] / float(imgSize[1]));
anari_cpp::setParameter(d, camera, "position", cam_pos);
anari_cpp::setParameter(d, camera, "direction", cam_dir);
anari_cpp::setParameter(d, camera, "up", cam_up);
anari_cpp::commitParameters(d, camera);
auto frame = anari_cpp::newObject<anari_cpp::Frame>(d);
anari_cpp::setParameter(d, frame, "size", imgSize);
anari_cpp::setParameter(d, frame, "channel.color", ANARI_FLOAT32_VEC4);
anari_cpp::setParameter(d, frame, "world", w);
anari_cpp::setParameter(d, frame, "camera", camera);
anari_cpp::setParameter(d, frame, "renderer", renderer);
anari_cpp::commitParameters(d, frame);
anari_cpp::release(d, camera);
anari_cpp::release(d, renderer);
anari_cpp::render(d, frame);
anari_cpp::wait(d, frame);
const auto fb = anari_cpp::map<vtkm::Vec4f_32>(d, frame, "channel.color");
vtkm::cont::DataSetBuilderUniform builder;
vtkm::cont::DataSet image = builder.Create(vtkm::Id2(fb.width, fb.height));
// NOTE: We are only copying the pixel data into a VTK-m array for the
// purpose of using VTK-m's image comparison test code. Applications
// would not normally do this and instead just use the pixel data
// directly, such as displaying it in an interactive window.
vtkm::cont::ArrayHandle<vtkm::Vec4f_32> colorArray =
vtkm::cont::make_ArrayHandle(fb.data, fb.width * fb.height, vtkm::CopyFlag::On);
anari_cpp::unmap(d, frame, "channel.color");
anari_cpp::release(d, frame);
image.AddPointField("color", colorArray);
VTKM_TEST_ASSERT(test_equal_images(image, imgName));
}
} // namespace
#endif

@ -0,0 +1,18 @@
##============================================================================
## 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.
##============================================================================
vtkm_unit_tests(
SOURCES
UnitTestANARIMapperGlyphs.cxx
UnitTestANARIMapperPoints.cxx
UnitTestANARIMapperTriangles.cxx
UnitTestANARIMapperVolume.cxx
UnitTestANARIScene.cxx
)

@ -0,0 +1,77 @@
//============================================================================
// 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.
//============================================================================
// vtkm::anari
#include <vtkm/interop/anari/ANARIMapperGlyphs.h>
// vtk-m
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/vector_analysis/Gradient.h>
#include <vtkm/io/EncodePNG.h>
#include <vtkm/source/Tangle.h>
// std
#include <cstdlib>
#include <vector>
#include "ANARITestCommon.h"
namespace
{
void RenderTests()
{
// Initialize ANARI /////////////////////////////////////////////////////////
auto d = loadANARIDevice();
// Create VTKm datasets /////////////////////////////////////////////////////
vtkm::source::Tangle source;
source.SetPointDimensions({ 32 });
auto tangle = source.Execute();
vtkm::filter::vector_analysis::Gradient gradientFilter;
gradientFilter.SetActiveField("tangle");
gradientFilter.SetOutputFieldName("Gradient");
auto tangleGrad = gradientFilter.Execute(tangle);
// Map data to ANARI objects ////////////////////////////////////////////////
auto world = anari_cpp::newObject<anari_cpp::World>(d);
vtkm::interop::anari::ANARIActor actor(
tangleGrad.GetCellSet(), tangleGrad.GetCoordinateSystem(), tangleGrad.GetField("Gradient"));
vtkm::interop::anari::ANARIMapperGlyphs mGlyphs(d, actor);
auto surface = mGlyphs.GetANARISurface();
anari_cpp::setParameterArray1D(d, world, "surface", &surface, 1);
anari_cpp::commitParameters(d, world);
// Render a frame ///////////////////////////////////////////////////////////
renderTestANARIImage(d,
world,
vtkm::Vec3f_32(0.5f, 1.f, 0.6f),
vtkm::Vec3f_32(0.f, -1.f, 0.f),
vtkm::Vec3f_32(0.f, 0.f, 1.f),
"interop/anari/glyphs.png");
// Cleanup //////////////////////////////////////////////////////////////////
anari_cpp::release(d, world);
anari_cpp::release(d, d);
}
} // namespace
int UnitTestANARIMapperGlyphs(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(RenderTests, argc, argv);
}

@ -0,0 +1,84 @@
//============================================================================
// 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.
//============================================================================
// vtkm::anari
#include <vtkm/interop/anari/ANARIMapperPoints.h>
// vtk-m
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/contour/Contour.h>
#include <vtkm/io/EncodePNG.h>
#include <vtkm/source/Tangle.h>
// std
#include <cstdlib>
#include <vector>
#include "ANARITestCommon.h"
namespace
{
void RenderTests()
{
// Initialize ANARI /////////////////////////////////////////////////////////
auto d = loadANARIDevice();
// Create VTKm datasets /////////////////////////////////////////////////////
vtkm::source::Tangle source;
source.SetPointDimensions({ 32 });
auto tangle = source.Execute();
auto& tangle_field = tangle.GetField("tangle");
vtkm::Range range;
tangle_field.GetRange(&range);
const auto isovalue = range.Center();
vtkm::filter::contour::Contour contourFilter;
contourFilter.SetIsoValue(isovalue);
contourFilter.SetActiveField("tangle");
auto tangleIso = contourFilter.Execute(tangle);
// Map data to ANARI objects ////////////////////////////////////////////////
auto world = anari_cpp::newObject<anari_cpp::World>(d);
vtkm::interop::anari::ANARIActor actor(
tangleIso.GetCellSet(), tangleIso.GetCoordinateSystem(), tangleIso.GetField("tangle"));
vtkm::interop::anari::ANARIMapperPoints mIso(d, actor);
setColorMap(d, mIso);
auto surface = mIso.GetANARISurface();
anari_cpp::setParameterArray1D(d, world, "surface", &surface, 1);
anari_cpp::commitParameters(d, world);
// Render a frame ///////////////////////////////////////////////////////////
renderTestANARIImage(d,
world,
vtkm::Vec3f_32(-0.05, 1.43, 1.87),
vtkm::Vec3f_32(0.32, -0.53, -0.79),
vtkm::Vec3f_32(-0.20, -0.85, 0.49),
"interop/anari/points.png");
// Cleanup //////////////////////////////////////////////////////////////////
anari_cpp::release(d, world);
anari_cpp::release(d, d);
}
} // namespace
int UnitTestANARIMapperPoints(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(RenderTests, argc, argv);
}

@ -0,0 +1,85 @@
//============================================================================
// 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.
//============================================================================
// vtkm::anari
#include <vtkm/interop/anari/ANARIMapperTriangles.h>
// vtk-m
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/contour/Contour.h>
#include <vtkm/io/EncodePNG.h>
#include <vtkm/source/Tangle.h>
// std
#include <cstdlib>
#include <vector>
#include "ANARITestCommon.h"
namespace
{
void RenderTests()
{
// Initialize ANARI /////////////////////////////////////////////////////////
auto d = loadANARIDevice();
// Create VTKm datasets /////////////////////////////////////////////////////
vtkm::source::Tangle source;
source.SetPointDimensions({ 32 });
auto tangle = source.Execute();
auto& tangle_field = tangle.GetField("tangle");
vtkm::Range range;
tangle_field.GetRange(&range);
const auto isovalue = range.Center();
vtkm::filter::contour::Contour contourFilter;
contourFilter.SetIsoValue(isovalue);
contourFilter.SetActiveField("tangle");
auto tangleIso = contourFilter.Execute(tangle);
// Map data to ANARI objects ////////////////////////////////////////////////
auto world = anari_cpp::newObject<anari_cpp::World>(d);
vtkm::interop::anari::ANARIActor actor(
tangleIso.GetCellSet(), tangleIso.GetCoordinateSystem(), tangleIso.GetField("tangle"));
vtkm::interop::anari::ANARIMapperTriangles mIso(d, actor);
mIso.SetCalculateNormals(true);
setColorMap(d, mIso);
auto surface = mIso.GetANARISurface();
anari_cpp::setParameterArray1D(d, world, "surface", &surface, 1);
anari_cpp::commitParameters(d, world);
// Render a frame ///////////////////////////////////////////////////////////
renderTestANARIImage(d,
world,
vtkm::Vec3f_32(-0.05, 1.43, 1.87),
vtkm::Vec3f_32(0.32, -0.53, -0.79),
vtkm::Vec3f_32(-0.20, -0.85, 0.49),
"interop/anari/isosurface.png");
// Cleanup //////////////////////////////////////////////////////////////////
anari_cpp::release(d, world);
anari_cpp::release(d, d);
}
} // namespace
int UnitTestANARIMapperTriangles(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(RenderTests, argc, argv);
}

@ -0,0 +1,74 @@
//============================================================================
// 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.
//============================================================================
// vtkm::anari
#include <vtkm/interop/anari/ANARIMapperVolume.h>
// vtk-m
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/contour/Contour.h>
#include <vtkm/io/EncodePNG.h>
#include <vtkm/source/Tangle.h>
// std
#include <cstdlib>
#include <vector>
#include "ANARITestCommon.h"
namespace
{
void RenderTests()
{
// Initialize ANARI /////////////////////////////////////////////////////////
auto d = loadANARIDevice();
// Create VTKm datasets /////////////////////////////////////////////////////
vtkm::source::Tangle source;
source.SetPointDimensions({ 32 });
auto tangle = source.Execute();
// Map data to ANARI objects ////////////////////////////////////////////////
auto world = anari_cpp::newObject<anari_cpp::World>(d);
vtkm::interop::anari::ANARIActor actor(
tangle.GetCellSet(), tangle.GetCoordinateSystem(), tangle.GetField("tangle"));
vtkm::interop::anari::ANARIMapperVolume mVol(d, actor);
setColorMap(d, mVol);
auto volume = mVol.GetANARIVolume();
anari_cpp::setParameterArray1D(d, world, "volume", &volume, 1);
anari_cpp::commitParameters(d, world);
// Render a frame ///////////////////////////////////////////////////////////
renderTestANARIImage(d,
world,
vtkm::Vec3f_32(-0.05, 1.43, 1.87),
vtkm::Vec3f_32(0.32, -0.53, -0.79),
vtkm::Vec3f_32(-0.20, -0.85, 0.49),
"interop/anari/volume.png");
// Cleanup //////////////////////////////////////////////////////////////////
anari_cpp::release(d, world);
anari_cpp::release(d, d);
}
} // namespace
int UnitTestANARIMapperVolume(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(RenderTests, argc, argv);
}

@ -0,0 +1,110 @@
//============================================================================
// 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.
//============================================================================
// vtkm::anari
#include <vtkm/interop/anari/ANARIMapperGlyphs.h>
#include <vtkm/interop/anari/ANARIMapperTriangles.h>
#include <vtkm/interop/anari/ANARIMapperVolume.h>
#include <vtkm/interop/anari/ANARIScene.h>
// vtk-m
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/contour/Contour.h>
#include <vtkm/filter/vector_analysis/Gradient.h>
#include <vtkm/io/EncodePNG.h>
#include <vtkm/source/Tangle.h>
// std
#include <cstdlib>
#include <vector>
#include "ANARITestCommon.h"
namespace
{
void RenderTests()
{
// Initialize ANARI /////////////////////////////////////////////////////////
auto d = loadANARIDevice();
// Create VTKm datasets /////////////////////////////////////////////////////
vtkm::source::Tangle source;
source.SetPointDimensions({ 32 });
auto tangle = source.Execute();
auto& tangle_field = tangle.GetField("tangle");
vtkm::Range range;
tangle_field.GetRange(&range);
const auto isovalue = range.Center();
vtkm::filter::contour::Contour contourFilter;
contourFilter.SetIsoValue(isovalue);
contourFilter.SetActiveField(tangle_field.GetName());
auto tangleIso = contourFilter.Execute(tangle);
vtkm::filter::vector_analysis::Gradient gradientFilter;
gradientFilter.SetActiveField(tangle_field.GetName());
gradientFilter.SetOutputFieldName("Gradient");
auto tangleGrad = gradientFilter.Execute(tangle);
// Map data to ANARI objects ////////////////////////////////////////////////
vtkm::interop::anari::ANARIScene scene(d);
auto& mVol = scene.AddMapper(vtkm::interop::anari::ANARIMapperVolume(d));
mVol.SetName("volume");
auto& mIso = scene.AddMapper(vtkm::interop::anari::ANARIMapperTriangles(d));
mIso.SetName("isosurface");
mIso.SetCalculateNormals(true);
auto& mGrad = scene.AddMapper(vtkm::interop::anari::ANARIMapperGlyphs(d));
mGrad.SetName("gradient");
// Render a frame ///////////////////////////////////////////////////////////
renderTestANARIImage(d,
scene.GetANARIWorld(),
vtkm::Vec3f_32(-0.05, 1.43, 1.87),
vtkm::Vec3f_32(0.32, -0.53, -0.79),
vtkm::Vec3f_32(-0.20, -0.85, 0.49),
"interop/anari/scene-empty-mappers.png");
// Render a frame ///////////////////////////////////////////////////////////
mVol.SetActor({ tangle.GetCellSet(), tangle.GetCoordinateSystem(), tangle.GetField("tangle") });
mIso.SetActor(
{ tangleIso.GetCellSet(), tangleIso.GetCoordinateSystem(), tangleIso.GetField("tangle") });
mGrad.SetActor(
{ tangleGrad.GetCellSet(), tangleGrad.GetCoordinateSystem(), tangleGrad.GetField("Gradient") });
setColorMap(d, mVol);
setColorMap(d, mIso);
setColorMap(d, mGrad);
renderTestANARIImage(d,
scene.GetANARIWorld(),
vtkm::Vec3f_32(-0.05, 1.43, 1.87),
vtkm::Vec3f_32(0.32, -0.53, -0.79),
vtkm::Vec3f_32(-0.20, -0.85, 0.49),
"interop/anari/scene.png");
// Cleanup //////////////////////////////////////////////////////////////////
anari_cpp::release(d, d);
}
} // namespace
int UnitTestANARIScene(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(RenderTests, argc, argv);
}

@ -0,0 +1,14 @@
NAME
vtkm_anari
GROUPS
ANARI
DEPENDS
vtkm_filter_field_conversion
vtkm_filter_vector_analysis
vtkm_rendering
vtkm_worklet
TEST_DEPENDS
vtkm_filter_vector_analysis
vtkm_filter_contour
vtkm_rendering_testing
vtkm_source