support unstructured cell (point-centered only)

This commit is contained in:
Qi Wu 2024-04-04 01:06:21 +00:00 committed by Jefferson Amstutz
parent 9c8d89c70b
commit 86e6e6e65b
4 changed files with 284 additions and 29 deletions

@ -188,8 +188,8 @@ static TriangleFieldArrays UnpackFields(vtkm::cont::ArrayHandle<vtkm::Id4> tris,
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>();
return f.GetNumberOfValues() == 0 || f.GetData().GetNumberOfComponentsFlat() != 1; // ||
// !f.GetData().CanConvert<AttributeHandleT>();
};
const bool emptyField1 = isFieldEmpty(fields[0]);
@ -197,14 +197,28 @@ static TriangleFieldArrays UnpackFields(vtkm::cont::ArrayHandle<vtkm::Id4> tris,
const bool emptyField3 = isFieldEmpty(fields[2]);
const bool emptyField4 = isFieldEmpty(fields[3]);
vtkm::cont::ArrayHandle<vtkm::Float32> floatField1;
vtkm::cont::ArrayHandle<vtkm::Float32> floatField2;
vtkm::cont::ArrayHandle<vtkm::Float32> floatField3;
vtkm::cont::ArrayHandle<vtkm::Float32> floatField4;
if (!emptyField1)
vtkm::cont::ArrayCopyShallowIfPossible(fields[0].GetData(), floatField1);
if (!emptyField2)
vtkm::cont::ArrayCopyShallowIfPossible(fields[1].GetData(), floatField2);
if (!emptyField3)
vtkm::cont::ArrayCopyShallowIfPossible(fields[2].GetData(), floatField3);
if (!emptyField4)
vtkm::cont::ArrayCopyShallowIfPossible(fields[3].GetData(), floatField4);
auto field1 =
emptyField1 ? AttributeHandleT{} : fields[0].GetData().AsArrayHandle<AttributeHandleT>();
emptyField1 ? AttributeHandleT{} : floatField1 /*.AsArrayHandle<AttributeHandleT>()*/;
auto field2 =
emptyField2 ? AttributeHandleT{} : fields[1].GetData().AsArrayHandle<AttributeHandleT>();
emptyField2 ? AttributeHandleT{} : floatField2 /*.AsArrayHandle<AttributeHandleT>()*/;
auto field3 =
emptyField3 ? AttributeHandleT{} : fields[2].GetData().AsArrayHandle<AttributeHandleT>();
emptyField3 ? AttributeHandleT{} : floatField3 /*.AsArrayHandle<AttributeHandleT>()*/;
auto field4 =
emptyField4 ? AttributeHandleT{} : fields[3].GetData().AsArrayHandle<AttributeHandleT>();
emptyField4 ? AttributeHandleT{} : floatField4 /*.AsArrayHandle<AttributeHandleT>()*/;
vtkm::Range field1Range = range;
vtkm::Range field2Range = range;
@ -242,10 +256,10 @@ static TriangleFieldArrays UnpackFields(vtkm::cont::ArrayHandle<vtkm::Id4> tris,
field4Range);
vtkm::worklet::DispatcherMapField<ExtractTriangleFields>(fieldsWorklet)
.Invoke(tris,
field1,
field2,
field3,
field4,
floatField1,
floatField2,
floatField3,
floatField4,
retval.Field1,
retval.Field2,
retval.Field3,
@ -330,6 +344,9 @@ void ANARIMapperTriangles::SetANARIColorMapValueRange(const vtkm::Vec2f_32& valu
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::setParameter(d, s, "outTransform", anari_cpp::math::mat4(anari_cpp::identity));
anari_cpp::setParameter(d, s, "inOffset", vtkm::Vec4f_32(0.f, 0.f, 0.f, 0.f));
anari_cpp::setParameter(d, s, "outOffset", vtkm::Vec4f_32(0.f, 0.f, 0.f, 0.f));
anari_cpp::commitParameters(d, s);
}
@ -374,9 +391,13 @@ anari_cpp::Surface ANARIMapperTriangles::GetANARISurface()
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, "filter", "nearest");
anari_cpp::setParameter(d, s, "wrapMode", "clampToEdge");
anari_cpp::setParameter(d, s, "name", this->MakeObjectName("colormap"));
anari_cpp::setParameter(d, s, "inTransform", anari_cpp::mat4(anari_cpp::identity));
anari_cpp::setParameter(d, s, "outTransform", anari_cpp::math::mat4(anari_cpp::identity));
anari_cpp::setParameter(d, s, "inOffset", vtkm::Vec4f_32(0.f, 0.f, 0.f, 0.f));
anari_cpp::setParameter(d, s, "outOffset", vtkm::Vec4f_32(0.f, 0.f, 0.f, 0.f));
anari_cpp::commitParameters(d, s);
this->SetANARIColorMapValueRange(vtkm::Vec2f_32(0.f, 10.f));

@ -8,7 +8,9 @@
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ArrayCopyDevice.h>
#include <vtkm/interop/anari/ANARIMapperVolume.h>
namespace vtkm
@ -74,8 +76,6 @@ 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;
@ -116,6 +116,36 @@ anari_cpp::Volume ANARIMapperVolume::GetANARIVolume()
return this->Handles->Volume;
}
// For the moment, we use ospray conventions
// uint8_t VKL_TETRAHEDRON = 10;
// uint8_t VKL_HEXAHEDRON = 12;
// uint8_t VKL_WEDGE = 13;
// uint8_t VKL_PYRAMID = 14;
//
struct ToAnariCellType
{
VTKM_EXEC_CONT vtkm::UInt8 operator()(vtkm::UInt8 shape) const
{
if (shape == vtkm::CELL_SHAPE_TETRA)
{
return 10;
}
else if (shape == vtkm::CELL_SHAPE_HEXAHEDRON)
{
return 14;
}
else if (shape == vtkm::CELL_SHAPE_WEDGE)
{
return 13;
}
else if (shape == vtkm::CELL_SHAPE_PYRAMID)
{
return 12;
}
return uint8_t(-1);
}
};
void ANARIMapperVolume::ConstructArrays(bool regenerate)
{
if (regenerate)
@ -139,13 +169,20 @@ void ANARIMapperVolume::ConstructArrays(bool regenerate)
this->Handles->ReleaseArrays();
if (!this->Handles->SpatialField)
{
this->Handles->SpatialField = anari_cpp::newObject<anari_cpp::SpatialField>(
this->GetDevice(), (isStructured && isScalar) ? "structuredRegular" : "unstructured");
}
// Structured regular volume data
if (isStructured && isScalar)
{
auto structuredCells = cells.AsCellSet<vtkm::cont::CellSetStructured<3>>();
auto pdims =
isPointBased ? structuredCells.GetPointDimensions() : structuredCells.GetCellDimensions();
VolumeArrays arrays;
StructuredVolumeArrays arrays;
auto d = this->GetDevice();
@ -160,13 +197,124 @@ void ANARIMapperVolume::ConstructArrays(bool regenerate)
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 =
std::memcpy(this->Handles->StructuredParameters.Dims, &dims, sizeof(dims));
std::memcpy(this->Handles->StructuredParameters.Origin, &bLower, sizeof(bLower));
std::memcpy(this->Handles->StructuredParameters.Spacing, &spacing, sizeof(spacing));
this->Handles->StructuredParameters.Data =
anari_cpp::newArray3D(d, ptr, NoopANARIDeleter, nullptr, dims[0], dims[1], dims[2]);
this->Arrays = arrays;
this->StructuredArrays = arrays;
this->Valid = true;
}
// Unstructured volume data
else
{
if (!isPointBased)
{
throw vtkm::cont::ErrorBadValue("Anari Unstructured volume data must be point-based.");
}
UntructuredVolumeArrays arrays;
// Cell Data
if (cells.IsType<vtkm::cont::CellSetSingleType<>>())
{
// 1. Cell Type
vtkm::cont::CellSetSingleType<> sgl = cells.AsCellSet<vtkm::cont::CellSetSingleType<>>();
auto shapes =
sgl.GetShapesArray(vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
vtkm::cont::ArrayCopyDevice(vtkm::cont::make_ArrayHandleTransform(shapes, ToAnariCellType{}),
arrays.CellType);
// 2. Cell Connectivity
auto conn =
sgl.GetConnectivityArray(vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
vtkm::cont::ArrayCopyDevice(conn, arrays.Index);
// 3. Cell Index
auto offsets =
sgl.GetOffsetsArray(vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
vtkm::cont::ArrayCopyDevice(offsets, arrays.CellIndex);
}
else if (cells.IsType<vtkm::cont::CellSetExplicit<>>())
{
// 1. Cell Type
vtkm::cont::CellSetExplicit<> exp = cells.AsCellSet<vtkm::cont::CellSetExplicit<>>();
auto shapes =
exp.GetShapesArray(vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
vtkm::cont::ArrayCopyDevice(vtkm::cont::make_ArrayHandleTransform(shapes, ToAnariCellType{}),
arrays.CellType);
// 2. Cell Connectivity
auto conn =
exp.GetConnectivityArray(vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
vtkm::cont::ArrayCopyDevice(conn, arrays.Index);
// 3. Cell Index
auto offsets =
exp.GetOffsetsArray(vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
vtkm::cont::ArrayCopyDevice(offsets, arrays.CellIndex);
}
// Vetrex Coordinates
vtkm::cont::ArrayCopyShallowIfPossible(coords.GetData(), arrays.VertexPosition);
// Vetrex Data
vtkm::cont::ArrayCopyShallowIfPossible(fieldArray, arrays.VertexData);
// Send data to ANARI
auto d = this->GetDevice();
// "indexPrefixed"
this->Handles->UnstructuredParameters.IndexPrefixed = false;
// "vertex.position"
{
auto* ptr =
(vtkm::Vec3f_32*)arrays.VertexPosition.GetBuffers()[0].ReadPointerHost(*arrays.Token);
this->Handles->UnstructuredParameters.VertexPosition = anari_cpp::newArray1D(
d, ptr, NoopANARIDeleter, nullptr, arrays.VertexPosition.GetNumberOfValues());
}
// "vertex.data"
{
auto* ptr = (float*)arrays.VertexData.GetBuffers()[0].ReadPointerHost(*arrays.Token);
this->Handles->UnstructuredParameters.VertexData = anari_cpp::newArray1D(
d, ptr, NoopANARIDeleter, nullptr, arrays.VertexData.GetNumberOfValues());
}
// "index"
{
auto* ptr = (uint64_t*)arrays.Index.GetBuffers()[0].ReadPointerHost(*arrays.Token);
this->Handles->UnstructuredParameters.Index =
anari_cpp::newArray1D(d, ptr, NoopANARIDeleter, nullptr, arrays.Index.GetNumberOfValues());
}
// "cell.index"
{
auto* ptr = (uint64_t*)arrays.CellIndex.GetBuffers()[0].ReadPointerHost(*arrays.Token);
this->Handles->UnstructuredParameters.CellIndex = anari_cpp::newArray1D(
d, ptr, NoopANARIDeleter, nullptr, arrays.CellIndex.GetNumberOfValues() - 1);
}
// TODO "cell.data" (NOT SUPPORED YET)
// {
// auto* ptr = (float*)arrays.CellData.GetBuffers()[0].ReadPointerHost(*arrays.Token);
// this->Handles->UnstructuredParameters.CellData =
// anari_cpp::newArray1D(d, ptr, NoopANARIDeleter, nullptr, arrays.CellData.GetNumberOfValues());
// }
// "cell.type"
{
uint8_t* ptr = (uint8_t*)arrays.CellType.GetBuffers()[0].ReadPointerHost(*arrays.Token);
this->Handles->UnstructuredParameters.CellType = anari_cpp::newArray1D(
d, ptr, NoopANARIDeleter, nullptr, arrays.CellType.GetNumberOfValues());
}
this->UnstructuredArrays = arrays;
this->Valid = true;
}
@ -185,16 +333,69 @@ void ANARIMapperVolume::UpdateSpatialField()
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "spacing");
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "data");
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "vertex.position");
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "vertex.data");
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "index");
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "indexPrefixed");
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "cell.index");
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "cell.data");
anari_cpp::unsetParameter(d, this->Handles->SpatialField, "cell.type");
anari_cpp::setParameter(
d, this->Handles->SpatialField, "name", this->MakeObjectName("spatialField"));
if (this->Handles->Parameters.Data)
if (this->Handles->StructuredParameters.Data)
{
anari_cpp::setParameter(
d, this->Handles->SpatialField, "origin", this->Handles->Parameters.Origin);
d, this->Handles->SpatialField, "origin", this->Handles->StructuredParameters.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);
d, this->Handles->SpatialField, "spacing", this->Handles->StructuredParameters.Spacing);
anari_cpp::setParameter(
d, this->Handles->SpatialField, "data", this->Handles->StructuredParameters.Data);
}
if (this->Handles->UnstructuredParameters.VertexPosition)
{
anari_cpp::setParameter(d,
this->Handles->SpatialField,
"vertex.position",
this->Handles->UnstructuredParameters.VertexPosition);
}
if (this->Handles->UnstructuredParameters.VertexData)
{
anari_cpp::setParameter(d,
this->Handles->SpatialField,
"vertex.data",
this->Handles->UnstructuredParameters.VertexData);
}
if (this->Handles->UnstructuredParameters.Index)
{
anari_cpp::setParameter(
d, this->Handles->SpatialField, "index", this->Handles->UnstructuredParameters.Index);
}
if (this->Handles->UnstructuredParameters.CellIndex)
{
anari_cpp::setParameter(d,
this->Handles->SpatialField,
"indexPrefixed",
this->Handles->UnstructuredParameters.IndexPrefixed);
}
if (this->Handles->UnstructuredParameters.CellIndex)
{
anari_cpp::setParameter(d,
this->Handles->SpatialField,
"cell.index",
this->Handles->UnstructuredParameters.CellIndex);
}
// if (this->Handles->UnstructuredParameters.CellData)
// {
// anari_cpp::setParameter(
// d, this->Handles->SpatialField, "cell.data", this->Handles->UnstructuredParameters.CellData);
// }
if (this->Handles->UnstructuredParameters.CellType)
{
anari_cpp::setParameter(
d, this->Handles->SpatialField, "cell.type", this->Handles->UnstructuredParameters.CellType);
}
anari_cpp::commitParameters(d, this->Handles->SpatialField);
@ -210,8 +411,11 @@ ANARIMapperVolume::ANARIHandles::~ANARIHandles()
void ANARIMapperVolume::ANARIHandles::ReleaseArrays()
{
anari_cpp::release(this->Device, this->Parameters.Data);
this->Parameters.Data = nullptr;
anari_cpp::release(this->Device, this->StructuredParameters.Data);
this->StructuredParameters.Data = nullptr;
anari_cpp::release(this->Device, this->UnstructuredParameters.VertexPosition);
this->UnstructuredParameters.VertexPosition = nullptr;
}
} // namespace anari

@ -22,7 +22,7 @@ namespace anari
/// @brief Raw ANARI arrays and parameter values set on the `ANARISpatialField`.
///
struct VolumeParameters
struct StructuredVolumeParameters
{
anari_cpp::Array3D Data{ nullptr };
int Dims[3];
@ -30,14 +30,36 @@ struct VolumeParameters
float Spacing[3];
};
struct UnstructuredVolumeParameters
{
anari_cpp::Array1D VertexPosition{ nullptr };
anari_cpp::Array1D VertexData{ nullptr };
anari_cpp::Array1D Index{ nullptr };
anari_cpp::Array1D CellIndex{ nullptr };
anari_cpp::Array1D CellData{ nullptr };
anari_cpp::Array1D CellType{ nullptr };
bool IndexPrefixed{ false };
};
/// @brief VTK-m data arrays underlying the `ANARIArray` handles created by the mapper.
///
struct VolumeArrays
struct StructuredVolumeArrays
{
vtkm::cont::ArrayHandle<vtkm::Float32> Data;
std::shared_ptr<vtkm::cont::Token> Token{ new vtkm::cont::Token };
};
struct UntructuredVolumeArrays
{
vtkm::cont::ArrayHandle<vtkm::Vec3f_32> VertexPosition;
vtkm::cont::ArrayHandle<vtkm::Float32> VertexData;
vtkm::cont::ArrayHandle<vtkm::UInt64> Index;
vtkm::cont::ArrayHandle<vtkm::UInt64> CellIndex;
vtkm::cont::ArrayHandle<vtkm::Float32> CellData;
vtkm::cont::ArrayHandle<vtkm::UInt8> CellType;
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
@ -92,13 +114,15 @@ private:
anari_cpp::Device Device{ nullptr };
anari_cpp::SpatialField SpatialField{ nullptr };
anari_cpp::Volume Volume{ nullptr };
VolumeParameters Parameters;
StructuredVolumeParameters StructuredParameters;
UnstructuredVolumeParameters UnstructuredParameters;
~ANARIHandles();
void ReleaseArrays();
};
std::shared_ptr<ANARIHandles> Handles;
VolumeArrays Arrays;
StructuredVolumeArrays StructuredArrays;
UntructuredVolumeArrays UnstructuredArrays;
};
} // namespace anari

@ -44,4 +44,10 @@ vtkm_library(
DEVICE_SOURCES ${device_sources}
)
vtkm_add_target_information(vtkm_anari
DROP_UNUSED_SYMBOLS
MODIFY_CUDA_FLAGS
DEVICE_SOURCES ANARIMapperVolume.cxx
)
target_link_libraries(vtkm_anari PUBLIC anari::anari)