Loading...
Searching...
No Matches
PointRasterizeFrustumImpl.h
Go to the documentation of this file.
// Copyright Contributors to the OpenVDB Project
// SPDX-License-Identifier: MPL-2.0
//
/// @author Dan Bailey
///
/// @file PointRasterizeFrustumImpl.h
///
#ifndef OPENVDB_POINTS_POINT_RASTERIZE_FRUSTUM_IMPL_HAS_BEEN_INCLUDED
#define OPENVDB_POINTS_POINT_RASTERIZE_FRUSTUM_IMPL_HAS_BEEN_INCLUDED
namespace openvdb {
OPENVDB_USE_VERSION_NAMESPACE
namespace OPENVDB_VERSION_NAME {
namespace points {
/// @cond OPENVDB_DOCS_INTERNAL
namespace point_rasterize_internal {
// By default this trait simply no-op copies the filter
template <typename FilterT>
struct RasterGroupTraits
{
using NewFilterT = FilterT;
static NewFilterT resolve(const FilterT& filter, const points::AttributeSet&)
{
return filter;
}
};
// For RasterGroups objects, this returns a new MultiGroupFilter with names-to-indices resolved
template <>
struct RasterGroupTraits<RasterGroups>
{
using NewFilterT = points::MultiGroupFilter;
static NewFilterT resolve(const RasterGroups& groups, const points::AttributeSet& attributeSet)
{
return NewFilterT(groups.includeNames, groups.excludeNames, attributeSet);
}
};
// Traits to indicate bool and ValueMask are bool types
template <typename T> struct BoolTraits { static const bool IsBool = false; };
template <> struct BoolTraits<bool> { static const bool IsBool = true; };
template <> struct BoolTraits<ValueMask> { static const bool IsBool = true; };
struct TrueOp {
bool mOn;
explicit TrueOp(double scale) : mOn(scale > 0.0) { }
template<typename ValueType>
void operator()(ValueType& v) const { v = static_cast<ValueType>(mOn); }
};
template <typename ValueT>
typename std::enable_if<std::is_integral<typename ValueTraits<ValueT>::ElementType>::value, ValueT>::type
castValue(const double value)
{
return ValueT(math::Ceil(value));
}
template <typename ValueT>
typename std::enable_if<!std::is_integral<typename ValueTraits<ValueT>::ElementType>::value, ValueT>::type
castValue(const double value)
{
return static_cast<ValueT>(value);
}
template <typename ValueT>
typename std::enable_if<!ValueTraits<ValueT>::IsVec, bool>::type
greaterThan(const ValueT& value, const float threshold)
{
return value >= static_cast<ValueT>(threshold);
}
template <typename ValueT>
typename std::enable_if<ValueTraits<ValueT>::IsVec, bool>::type
greaterThan(const ValueT& value, const float threshold)
{
return static_cast<double>(value.lengthSqr()) >= threshold*threshold;
}
template <typename AttributeT, typename HandleT, typename StridedHandleT>
typename std::enable_if<!ValueTraits<AttributeT>::IsVec, AttributeT>::type
getAttributeScale(HandleT& handlePtr, StridedHandleT&, Index index)
{
if (handlePtr) {
return handlePtr->get(index);
}
return AttributeT(1);
}
template <typename AttributeT, typename HandleT, typename StridedHandleT>
typename std::enable_if<ValueTraits<AttributeT>::IsVec, AttributeT>::type
getAttributeScale(HandleT& handlePtr, StridedHandleT& stridedHandlePtr, Index index)
{
if (handlePtr) {
return handlePtr->get(index);
} else if (stridedHandlePtr) {
return AttributeT(
stridedHandlePtr->get(index, 0),
stridedHandlePtr->get(index, 1),
stridedHandlePtr->get(index, 2));
}
return AttributeT(1);
}
template <typename ValueT>
struct MultiplyOp
{
template <typename AttributeT>
static ValueT mul(const ValueT& a, const AttributeT& b)
{
return a * b;
}
};
template <>
struct MultiplyOp<bool>
{
template <typename AttributeT>
static bool mul(const bool& a, const AttributeT& b)
{
return a && (b != zeroVal<AttributeT>());
}
};
template <typename PointDataGridT, typename AttributeT, typename GridT,
typename FilterT>
struct RasterizeOp
{
using TreeT = typename GridT::TreeType;
using LeafT = typename TreeT::LeafNodeType;
using ValueT = typename TreeT::ValueType;
using PointLeafT = typename PointDataGridT::TreeType::LeafNodeType;
using PointIndexT = typename PointLeafT::ValueType;
using CombinableT = tbb::combinable<GridT>;
using SumOpT = tools::valxform::SumOp<ValueT>;
using MaxOpT = tools::valxform::MaxOp<ValueT>;
using PositionHandleT = openvdb::points::AttributeHandle<Vec3f>;
using VelocityHandleT = openvdb::points::AttributeHandle<Vec3f>;
using RadiusHandleT = openvdb::points::AttributeHandle<float>;
// to prevent checking the interrupter too frequently, only check every 32 voxels cubed
static const int interruptThreshold = 32*32*32;
RasterizeOp(
const PointDataGridT& grid,
const std::vector<Index64>& offsets,
const size_t attributeIndex,
const Name& velocityAttribute,
const Name& radiusAttribute,
CombinableT& combinable,
CombinableT* weightCombinable,
const bool dropBuffers,
const float scale,
const FilterT& filter,
const bool computeMax,
const bool alignedTransform,
const bool staticCamera,
const FrustumRasterizerSettings& settings,
const FrustumRasterizerMask& mask,
util::NullInterrupter* interrupt)
: mGrid(grid)
, mOffsets(offsets)
, mAttributeIndex(attributeIndex)
, mVelocityAttribute(velocityAttribute)
, mRadiusAttribute(radiusAttribute)
, mCombinable(combinable)
, mWeightCombinable(weightCombinable)
, mDropBuffers(dropBuffers)
, mScale(scale)
, mFilter(filter)
, mComputeMax(computeMax)
, mAlignedTransform(alignedTransform)
, mStaticCamera(staticCamera)
, mSettings(settings)
, mMask(mask)
, mInterrupter(interrupt) { }
template <typename PointOpT>
static void rasterPoint(const Coord& ijk, const double scale,
const AttributeT& attributeScale, PointOpT& op)
{
op(ijk, scale, attributeScale);
}
template <typename PointOpT>
static void rasterPoint(const Vec3d& position, const double scale,
const AttributeT& attributeScale, PointOpT& op)
{
Coord ijk = Coord::round(position);
op(ijk, scale, attributeScale);
}
template <typename SphereOpT>
static void rasterVoxelSphere(const Vec3d& position, const double scale,
const AttributeT& attributeScale, const float radius, util::NullInterrupter* interrupter, SphereOpT& op)
{
assert(radius > 0.0f);
Coord ijk = Coord::round(position);
int &i = ijk[0], &j = ijk[1], &k = ijk[2];
const int imin=math::Floor(position[0]-radius), imax=math::Ceil(position[0]+radius);
const int jmin=math::Floor(position[1]-radius), jmax=math::Ceil(position[1]+radius);
const int kmin=math::Floor(position[2]-radius), kmax=math::Ceil(position[2]+radius);
const bool interrupt = interrupter && (imax-imin)*(jmax-jmin)*(kmax-kmin) > interruptThreshold;
for (i = imin; i <= imax; ++i) {
if (interrupt && interrupter->wasInterrupted()) break;
const auto x2 = math::Pow2(i - position[0]);
for (j = jmin; j <= jmax; ++j) {
if (interrupt && interrupter->wasInterrupted()) break;
const auto x2y2 = math::Pow2(j - position[1]) + x2;
for (k = kmin; k <= kmax; ++k) {
const auto x2y2z2 = x2y2 + math::Pow2(k - position[2]);
op(ijk, scale, attributeScale, x2y2z2, radius*radius);
}
}
}
}
template <typename SphereOpT>
static void rasterApproximateFrustumSphere(const Vec3d& position, const double scale,
const AttributeT& attributeScale, const float radiusWS,
const math::Transform& frustum, const CoordBBox* clipBBox,
util::NullInterrupter* interrupter, SphereOpT& op)
{
Vec3d voxelSize = frustum.voxelSize(position);
Vec3d radius = Vec3d(radiusWS)/voxelSize;
CoordBBox frustumBBox(Coord::floor(position-radius), Coord::ceil(position+radius));
// if clipping to frustum is enabled, clip the index bounding box
if (clipBBox) {
frustumBBox.intersect(*clipBBox);
}
Vec3i outMin = frustumBBox.min().asVec3i();
Vec3i outMax = frustumBBox.max().asVec3i();
const bool interrupt = interrupter && frustumBBox.volume() > interruptThreshold;
// back-project each output voxel into the input tree.
Vec3R xyz;
Coord outXYZ;
int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
for (x = outMin.x(); x <= outMax.x(); ++x) {
if (interrupt && interrupter->wasInterrupted()) break;
xyz.x() = x;
for (y = outMin.y(); y <= outMax.y(); ++y) {
if (interrupt && interrupter->wasInterrupted()) break;
xyz.y() = y;
for (z = outMin.z(); z <= outMax.z(); ++z) {
xyz.z() = z;
// approximate conversion to world-space
Vec3d offset = (xyz - position) * voxelSize;
double distanceSqr = offset.dot(offset);
op(outXYZ, scale, attributeScale, distanceSqr, radiusWS*radiusWS);
}
}
}
}
template <typename SphereOpT>
static void rasterFrustumSphere(const Vec3d& position, const double scale,
const AttributeT& attributeScale, const float radiusWS,
const math::Transform& frustum, const CoordBBox* clipBBox,
util::NullInterrupter* interrupter, SphereOpT& op)
{
const Vec3d positionWS = frustum.indexToWorld(position);
BBoxd inputBBoxWS(positionWS-radiusWS, positionWS+radiusWS);
// Transform the corners of the input tree's bounding box
// and compute the enclosing bounding box in the output tree.
Vec3R frustumMin;
Vec3R frustumMax;
math::calculateBounds(frustum, inputBBoxWS.min(), inputBBoxWS.max(),
frustumMin, frustumMax);
CoordBBox frustumBBox(Coord::floor(frustumMin), Coord::ceil(frustumMax));
// if clipping to frustum is enabled, clip the index bounding box
if (clipBBox) {
frustumBBox.intersect(*clipBBox);
}
Vec3i outMin = frustumBBox.min().asVec3i();
Vec3i outMax = frustumBBox.max().asVec3i();
const bool interrupt = interrupter && frustumBBox.volume() > interruptThreshold;
// back-project each output voxel into the input tree.
Vec3R xyz;
Coord outXYZ;
int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
for (x = outMin.x(); x <= outMax.x(); ++x) {
if (interrupt && interrupter->wasInterrupted()) break;
xyz.x() = x;
for (y = outMin.y(); y <= outMax.y(); ++y) {
if (interrupt && interrupter->wasInterrupted()) break;
xyz.y() = y;
for (z = outMin.z(); z <= outMax.z(); ++z) {
xyz.z() = z;
Vec3R xyzWS = frustum.indexToWorld(xyz);
double xDist = xyzWS.x() - positionWS.x();
double yDist = xyzWS.y() - positionWS.y();
double zDist = xyzWS.z() - positionWS.z();
double distanceSqr = xDist*xDist+yDist*yDist+zDist*zDist;
op(outXYZ, scale, attributeScale, distanceSqr, radiusWS*radiusWS);
}
}
}
}
void operator()(const PointLeafT& leaf, size_t) const
{
if (mInterrupter && mInterrupter->wasInterrupted()) {
thread::cancelGroupExecution();
return;
}
using AccessorT = tree::ValueAccessor<typename GridT::TreeType>;
using MaskAccessorT = tree::ValueAccessor<const MaskTree>;
constexpr bool isBool = BoolTraits<ValueT>::IsBool;
const bool isFrustum = !mSettings.transform->isLinear();
const bool useRaytrace = mSettings.velocityMotionBlur || !mStaticCamera;
const bool useRadius = mSettings.useRadius;
const float radiusScale = mSettings.radiusScale;
const float radiusThreshold = std::sqrt(3.0f);
const float threshold = mSettings.threshold;
const bool scaleByVoxelVolume = !useRadius && mSettings.scaleByVoxelVolume;
const float shutterStartDt = mSettings.camera.shutterStart()/mSettings.framesPerSecond;
const float shutterEndDt = mSettings.camera.shutterEnd()/mSettings.framesPerSecond;
const int motionSteps = std::max(1, mSettings.motionSamples-1);
std::vector<Vec3d> motionPositions(motionSteps+1, Vec3d());
std::vector<Vec2s> frustumRadiusSizes(motionSteps+1, Vec2s());
const auto& pointsTransform = mGrid.constTransform();
const float voxelSize = static_cast<float>(mSettings.transform->voxelSize()[0]);
auto& grid = mCombinable.local();
auto& tree = grid.tree();
const auto& transform = *(mSettings.transform);
grid.setTransform(mSettings.transform->copy());
AccessorT valueAccessor(tree);
std::unique_ptr<MaskAccessorT> maskAccessor;
auto maskTree = mMask.getTreePtr();
if (maskTree) maskAccessor.reset(new MaskAccessorT(*maskTree));
const CoordBBox* clipBBox = !mMask.clipBBox().empty() ? &mMask.clipBBox() : nullptr;
std::unique_ptr<AccessorT> weightAccessor;
if (mWeightCombinable) {
auto& weightGrid = mWeightCombinable->local();
auto& weightTree = weightGrid.tree();
weightAccessor.reset(new AccessorT(weightTree));
}
// create a temporary tree when rasterizing with radius and accumulate
// or weighted average methods
std::unique_ptr<TreeT> tempTree;
std::unique_ptr<TreeT> tempWeightTree;
std::unique_ptr<AccessorT> tempValueAccessor;
std::unique_ptr<AccessorT> tempWeightAccessor;
if (useRadius && !mComputeMax) {
tempTree.reset(new TreeT(tree.background()));
tempValueAccessor.reset(new AccessorT(*tempTree));
if (weightAccessor) {
tempWeightTree.reset(new TreeT(weightAccessor->tree().background()));
tempWeightAccessor.reset(new AccessorT(*tempWeightTree));
}
}
// point rasterization
// impl - modify a single voxel by coord, handles temporary trees and all supported modes
auto doModifyVoxelOp = [&](const Coord& ijk, const double scale, const AttributeT& attributeScale,
const bool isTemp, const bool forceSum)
{
if (mMask && !mMask.valid(ijk, maskAccessor.get())) return;
if (isBool) {
// only modify the voxel if the attributeScale is positive and non-zero
if (!math::isZero<AttributeT>(attributeScale) && !math::isNegative<AttributeT>(attributeScale)) {
valueAccessor.modifyValue(ijk, TrueOp(scale));
}
} else {
ValueT weightValue = castValue<ValueT>(scale);
ValueT newValue = MultiplyOp<ValueT>::mul(weightValue, attributeScale);
if (scaleByVoxelVolume) {
newValue /= static_cast<ValueT>(transform.voxelSize(ijk.asVec3d()).product());
}
if (point_rasterize_internal::greaterThan(newValue, threshold)) {
if (isTemp) {
tempValueAccessor->modifyValue(ijk, MaxOpT(newValue));
if (tempWeightAccessor) {
tempWeightAccessor->modifyValue(ijk, MaxOpT(weightValue));
}
} else {
if (mComputeMax && !forceSum) {
valueAccessor.modifyValue(ijk, MaxOpT(newValue));
} else {
valueAccessor.modifyValue(ijk, SumOpT(newValue));
}
if (weightAccessor) {
weightAccessor->modifyValue(ijk, SumOpT(weightValue));
}
}
}
}
};
// modify a single voxel by coord, disable temporary trees
auto modifyVoxelOp = [&](const Coord& ijk, const double scale, const AttributeT& attributeScale)
{
doModifyVoxelOp(ijk, scale, attributeScale, /*isTemp=*/false, /*forceSum=*/false);
};
// sum a single voxel by coord, no temporary trees or maximum mode
auto sumVoxelOp = [&](const Coord& ijk, const double scale, const AttributeT& attributeScale)
{
doModifyVoxelOp(ijk, scale, attributeScale, /*isTemp=*/false, /*forceSum=*/true);
};
// sphere rasterization
// impl - modify a single voxel by coord based on distance from sphere origin
auto doModifyVoxelByDistanceOp = [&](const Coord& ijk, const double scale, const AttributeT& attributeScale,
const double distanceSqr, const double radiusSqr, const bool isTemp)
{
if (distanceSqr >= radiusSqr) return;
if (isBool) {
valueAccessor.modifyValue(ijk, TrueOp(scale));
} else {
double distance = std::sqrt(distanceSqr);
double radius = std::sqrt(radiusSqr);
double result = 1.0 - distance/radius;
doModifyVoxelOp(ijk, result * scale, attributeScale, isTemp, /*forceSum=*/false);
}
};
// modify a single voxel by coord based on distance from sphere origin, disable temporary trees
auto modifyVoxelByDistanceOp = [&](const Coord& ijk, const double scale, const AttributeT& attributeScale,
const double distanceSqr, const double radiusSqr)
{
doModifyVoxelByDistanceOp(ijk, scale, attributeScale, distanceSqr, radiusSqr, /*isTemp=*/false);
};
// modify a single voxel by coord based on distance from sphere origin, enable temporary trees
auto modifyTempVoxelByDistanceOp = [&](const Coord& ijk, const double scale, const AttributeT& attributeScale,
const double distanceSqr, const double radiusSqr)
{
doModifyVoxelByDistanceOp(ijk, scale, attributeScale, distanceSqr, radiusSqr, /*isTemp=*/true);
};
typename points::AttributeHandle<AttributeT>::Ptr attributeHandle;
using ElementT = typename ValueTraits<AttributeT>::ElementType;
typename points::AttributeHandle<ElementT>::Ptr stridedAttributeHandle;
if (mAttributeIndex != points::AttributeSet::INVALID_POS) {
const auto& attributeArray = leaf.constAttributeArray(mAttributeIndex);
if (attributeArray.stride() == 3) {
stridedAttributeHandle = points::AttributeHandle<ElementT>::create(attributeArray);
} else {
attributeHandle = points::AttributeHandle<AttributeT>::create(attributeArray);
}
}
size_t positionIndex = leaf.attributeSet().find("P");
size_t velocityIndex = leaf.attributeSet().find(mVelocityAttribute);
size_t radiusIndex = leaf.attributeSet().find(mRadiusAttribute);
auto positionHandle = PositionHandleT::create(
leaf.constAttributeArray(positionIndex));
auto velocityHandle = (useRaytrace && leaf.hasAttribute(velocityIndex)) ?
VelocityHandleT::create(leaf.constAttributeArray(velocityIndex)) :
VelocityHandleT::Ptr();
auto radiusHandle = (useRadius && leaf.hasAttribute(radiusIndex)) ?
RadiusHandleT::create(leaf.constAttributeArray(radiusIndex)) :
RadiusHandleT::Ptr();
for (auto iter = leaf.beginIndexOn(mFilter); iter; ++iter) {
// read attribute value if it exists, attributes with stride 3 are composited
// into a vector grid such as float[3] => vec3s
const AttributeT attributeScale = getAttributeScale<AttributeT>(
attributeHandle, stridedAttributeHandle, *iter);
float radiusWS = 1.0f, radius = 1.0f;
if (useRadius) {
radiusWS *= radiusScale;
if (radiusHandle) radiusWS *= radiusHandle->get(*iter);
if (isFrustum) {
radius = radiusWS;
} else if (voxelSize > 0.0f) {
radius = radiusWS / voxelSize;
}
}
// frustum thresholding is done later to factor in changing voxel sizes
bool doRadius = useRadius;
if (!isFrustum && radius < radiusThreshold) {
doRadius = false;
}
float increment = shutterEndDt - shutterStartDt;
// disable ray-tracing if velocity is very small
openvdb::Vec3f velocity(0.0f);
bool doRaytrace = useRaytrace;
if (doRaytrace) {
if (increment < openvdb::math::Delta<float>::value()) {
doRaytrace = false;
} else {
if (velocityHandle) velocity = velocityHandle->get(*iter);
if (mStaticCamera && velocity.lengthSqr() < openvdb::math::Delta<float>::value()) {
doRaytrace = false;
}
}
}
if (motionSteps > 1) increment /= float(motionSteps);
Vec3d position = positionHandle->get(*iter) + iter.getCoord().asVec3d();
if (doRaytrace) {
// raytracing is done in index space
position = pointsTransform.indexToWorld(position);
for (int motionStep = 0; motionStep <= motionSteps; motionStep++) {
float offset = motionStep == motionSteps ? shutterEndDt :
(shutterStartDt + increment * static_cast<float>(motionStep));
Vec3d samplePosition = position + velocity * offset;
const math::Transform* sampleTransform = &transform;
if (!mSettings.camera.isStatic()) {
sampleTransform = &mSettings.camera.transform(motionStep);
if (mSettings.camera.hasWeight(motionStep)) {
const float weight = mSettings.camera.weight(motionStep);
const Vec3d referencePosition = transform.worldToIndex(samplePosition);
const Vec3d adjustedPosition = sampleTransform->worldToIndex(samplePosition);
motionPositions[motionStep] = referencePosition + (adjustedPosition - referencePosition) * weight;
} else {
motionPositions[motionStep] = sampleTransform->worldToIndex(samplePosition);
}
} else {
motionPositions[motionStep] = sampleTransform->worldToIndex(samplePosition);
}
if (doRadius && isFrustum) {
Vec3d left = sampleTransform->worldToIndex(samplePosition - Vec3d(radiusWS, 0, 0));
Vec3d right = sampleTransform->worldToIndex(samplePosition + Vec3d(radiusWS, 0, 0));
float width = static_cast<float>((right - left).length());
Vec3d top = sampleTransform->worldToIndex(samplePosition + Vec3d(0, radiusWS, 0));
Vec3d bottom = sampleTransform->worldToIndex(samplePosition - Vec3d(0, radiusWS, 0));
float height = static_cast<float>((top - bottom).length());
frustumRadiusSizes[motionStep].x() = width;
frustumRadiusSizes[motionStep].y() = height;
}
}
double totalDistance = 0.0;
for (size_t i = 0; i < motionPositions.size()-1; i++) {
Vec3d direction = motionPositions[i+1] - motionPositions[i];
double distance = direction.length();
totalDistance += distance;
}
double distanceWeight = totalDistance > 0.0 ? 1.0 / totalDistance : 1.0;
if (doRadius && !mComputeMax) {
// mark all voxels inactive
for (auto leaf = tempTree->beginLeaf(); leaf; ++leaf) {
leaf->setValuesOff();
}
if (tempWeightAccessor) {
for (auto leaf = tempWeightTree->beginLeaf(); leaf; ++leaf) {
leaf->setValuesOff();
}
}
}
for (int motionStep = 0; motionStep < motionSteps; motionStep++) {
Vec3d startPosition = motionPositions[motionStep];
Vec3d endPosition = motionPositions[motionStep+1];
Vec3d direction(endPosition - startPosition);
double distance = direction.length();
// if rasterizing into a frustum grid, compute the index-space radii for start
// and end positions and if below the radius threshold disable using radius
float maxRadius = radius;
if (doRadius && isFrustum) {
const Vec2s& startRadius = frustumRadiusSizes[motionStep];
const Vec2s& endRadius = frustumRadiusSizes[motionStep+1];
if (startRadius[0] < radiusThreshold && startRadius[1] < radiusThreshold &&
endRadius[0] < radiusThreshold && endRadius[1] < radiusThreshold) {
doRadius = false;
} else {
// max radius is the largest index-space radius factoring in
// that in frustum space a sphere is not rasterized as spherical
maxRadius = std::max(startRadius[0], startRadius[1]);
maxRadius = std::max(maxRadius, endRadius[0]);
maxRadius = std::max(maxRadius, endRadius[1]);
}
}
if (doRadius) {
distanceWeight = std::min(distanceWeight, 1.0);
// these arbitrary constants are how tightly spheres are packed together
// irrespective of how large they are in index-space - if it is too low, the shape of
// the individual spheres becomes visible in the rasterized path,
// if it is too high, rasterization becomes less efficient with
// diminishing returns towards the accuracy of the rasterized path
double spherePacking = mSettings.accurateSphereMotionBlur ? 4.0 : 2.0;
const int steps = std::max(2, math::Ceil(distance * spherePacking / double(maxRadius)) + 1);
Vec3d sample(startPosition);
const Vec3d offset(direction / (steps-1));
for (int step = 0; step < steps; step++) {
if (isFrustum) {
if (mComputeMax) {
if (mSettings.accurateFrustumRadius) {
this->rasterFrustumSphere(sample, mScale * distanceWeight,
attributeScale, radiusWS, transform, clipBBox, mInterrupter, modifyVoxelByDistanceOp);
} else {
this->rasterApproximateFrustumSphere(sample, mScale * distanceWeight,
attributeScale, radiusWS, transform, clipBBox, mInterrupter, modifyVoxelByDistanceOp);
}
} else {
if (mSettings.accurateFrustumRadius) {
this->rasterFrustumSphere(sample, mScale * distanceWeight,
attributeScale, radiusWS, transform, clipBBox, mInterrupter, modifyTempVoxelByDistanceOp);
} else {
this->rasterApproximateFrustumSphere(sample, mScale * distanceWeight,
attributeScale, radiusWS, transform, clipBBox, mInterrupter, modifyTempVoxelByDistanceOp);
}
}
} else {
if (mComputeMax) {
this->rasterVoxelSphere(sample, mScale * distanceWeight,
attributeScale, radius, mInterrupter, modifyVoxelByDistanceOp);
} else {
this->rasterVoxelSphere(sample, mScale * distanceWeight,
attributeScale, radius, mInterrupter, modifyTempVoxelByDistanceOp);
}
}
if (step < (steps-1)) sample += offset;
else sample = endPosition;
}
} else {
// compute direction and store vector length as max time
mDdaRay.setMinTime(math::Delta<double>::value());
mDdaRay.setMaxTime(std::max(distance, math::Delta<double>::value()*2.0));
// DDA requires normalized directions
direction.normalize();
mDdaRay.setDir(direction);
// dda assumes node-centered ray-tracing, so compensate by adding half a voxel first
mDdaRay.setEye(startPosition + Vec3d(0.5));
// clip the ray to the frustum bounding box
if (clipBBox) {
mDdaRay.clip(*clipBBox);
}
// first rasterization in a subsequent DDA traversal should always sum contributions
// in order to smoothly stitch the beginning and end of two rays together
bool forceSum = motionStep > 0;
mDda.init(mDdaRay);
while (true) {
const Coord& voxel = mDda.voxel();
double delta = (mDda.next() - mDda.time()) * distanceWeight;
if (forceSum) {
this->rasterPoint(voxel, mScale * delta,
attributeScale, sumVoxelOp);
forceSum = false;
} else {
this->rasterPoint(voxel, mScale * delta,
attributeScale, modifyVoxelOp);
}
if (!mDda.step()) break;
}
}
}
if (doRadius && !mComputeMax) {
// copy values into valueAccessor
for (auto iter = tempTree->cbeginValueOn(); iter; ++iter) {
valueAccessor.modifyValue(iter.getCoord(), SumOpT(*iter));
}
// copy values into weightAccessor
if (weightAccessor) {
for (auto iter = tempWeightTree->cbeginValueOn(); iter; ++iter) {
weightAccessor->modifyValue(iter.getCoord(), SumOpT(*iter));
}
}
}
} else {
if (!mAlignedTransform) {
position = transform.worldToIndex(
pointsTransform.indexToWorld(position));
}
if (doRadius) {
if (isFrustum) {
if (mSettings.accurateFrustumRadius) {
this->rasterFrustumSphere(position, mScale, attributeScale,
radiusWS, transform, clipBBox, mInterrupter, modifyVoxelByDistanceOp);
} else {
this->rasterApproximateFrustumSphere(position, mScale, attributeScale,
radiusWS, transform, clipBBox, mInterrupter, modifyVoxelByDistanceOp);
}
} else {
this->rasterVoxelSphere(position, mScale, attributeScale,
radius, mInterrupter, modifyVoxelByDistanceOp);
}
} else {
this->rasterPoint(position, mScale, attributeScale, modifyVoxelOp);
}
}
}
// if drop buffers is enabled, swap the leaf buffer with a partial (empty) buffer,
// so the voxel data is de-allocated to reduce memory
if (mDropBuffers) {
typename PointLeafT::Buffer emptyBuffer(PartialCreate(), zeroVal<PointIndexT>());
(const_cast<PointLeafT&>(leaf)).swap(emptyBuffer);
}
}
// TODO: would be better to move some of the immutable values into a shared struct
private:
mutable math::Ray<double> mDdaRay;
mutable math::DDA<openvdb::math::Ray<double>> mDda;
const PointDataGridT& mGrid;
const std::vector<Index64>& mOffsets;
const size_t mAttributeIndex;
const Name mVelocityAttribute;
const Name mRadiusAttribute;
CombinableT& mCombinable;
CombinableT* mWeightCombinable;
const bool mDropBuffers;
const float mScale;
const FilterT& mFilter;
const bool mComputeMax;
const bool mAlignedTransform;
const bool mStaticCamera;
const FrustumRasterizerSettings& mSettings;
const FrustumRasterizerMask& mMask;
util::NullInterrupter* mInterrupter;
}; // struct RasterizeOp
/// @brief Combines multiple grids into one by stealing leaf nodes and summing voxel values
/// This class is designed to work with thread local storage containers such as tbb::combinable
template<typename GridT>
struct GridCombinerOp
{
using CombinableT = typename tbb::combinable<GridT>;
using TreeT = typename GridT::TreeType;
using LeafT = typename TreeT::LeafNodeType;
using ValueType = typename TreeT::ValueType;
using SumOpT = tools::valxform::SumOp<typename TreeT::ValueType>;
using MaxOpT = tools::valxform::MaxOp<typename TreeT::ValueType>;
GridCombinerOp(GridT& grid, bool sum)
: mTree(grid.tree())
, mSum(sum) {}
void operator()(const GridT& grid)
{
for (auto leaf = grid.tree().beginLeaf(); leaf; ++leaf) {
auto* newLeaf = mTree.probeLeaf(leaf->origin());
if (!newLeaf) {
// if the leaf doesn't yet exist in the new tree, steal it
auto& tree = const_cast<GridT&>(grid).tree();
mTree.addLeaf(tree.template stealNode<LeafT>(leaf->origin(),
zeroVal<ValueType>(), false));
}
else {
// otherwise increment existing values
if (mSum) {
for (auto iter = leaf->cbeginValueOn(); iter; ++iter) {
newLeaf->modifyValue(iter.offset(), SumOpT(ValueType(*iter)));
}
} else {
for (auto iter = leaf->cbeginValueOn(); iter; ++iter) {
newLeaf->modifyValue(iter.offset(), MaxOpT(ValueType(*iter)));
}
}
}
}
}
private:
TreeT& mTree;
bool mSum;
}; // struct GridCombinerOp
template<typename GridT>
struct CombinableTraits {
using OpT = GridCombinerOp<GridT>;
using T = typename OpT::CombinableT;
};
template <typename PointDataGridT>
class GridToRasterize
{
public:
using GridPtr = typename PointDataGridT::Ptr;
using GridConstPtr = typename PointDataGridT::ConstPtr;
using PointDataTreeT = typename PointDataGridT::TreeType;
using PointDataLeafT = typename PointDataTreeT::LeafNodeType;
GridToRasterize(GridPtr& grid, bool stream,
const FrustumRasterizerSettings& settings,
const FrustumRasterizerMask& mask)
: mGrid(grid)
, mStream(stream)
, mSettings(settings)
, mMask(mask) { }
GridToRasterize(GridConstPtr& grid, const FrustumRasterizerSettings& settings,
const FrustumRasterizerMask& mask)
: mGrid(ConstPtrCast<PointDataGridT>(grid))
, mStream(false)
, mSettings(settings)
, mMask(mask) { }
const typename PointDataGridT::TreeType& tree() const
{
return mGrid->constTree();
}
void setLeafPercentage(int percentage)
{
mLeafPercentage = math::Clamp(percentage, 0, 100);
}
int leafPercentage() const
{
return mLeafPercentage;
}
size_t memUsage() const
{
return mGrid->memUsage() + mLeafOffsets.capacity();
}
template <typename AttributeT, typename GridT, typename FilterT>
void rasterize(const Name& attribute,
typename CombinableTraits<GridT>::T& combiner, typename CombinableTraits<GridT>::T* weightCombiner,
const float scale, const FilterT& groupFilter, const bool computeMax, const bool reduceMemory,
util::NullInterrupter* interrupter)
{
using point_rasterize_internal::RasterizeOp;
const auto& velocityAttribute = mSettings.velocityMotionBlur ?
mSettings.velocityAttribute : "";
const auto& radiusAttribute = mSettings.useRadius ?
mSettings.radiusAttribute : "";
bool isPositionAttribute = attribute == "P";
bool isVelocityAttribute = attribute == mSettings.velocityAttribute;
bool isRadiusAttribute = attribute == mSettings.radiusAttribute;
// find the attribute index
const auto& attributeSet = mGrid->constTree().cbeginLeaf()->attributeSet();
const size_t attributeIndex = attributeSet.find(attribute);
const bool attributeExists = attributeIndex != points::AttributeSet::INVALID_POS;
if (attributeExists)
{
// throw if attribute type doesn't match AttributeT
const auto* attributeArray = attributeSet.getConst(attributeIndex);
const Index stride = bool(attributeArray) ? attributeArray->stride() : Index(1);
const auto& actualValueType = attributeSet.descriptor().valueType(attributeIndex);
const auto requestedValueType = Name(typeNameAsString<AttributeT>());
const bool packVector = stride == 3 &&
((actualValueType == typeNameAsString<float>() &&
requestedValueType == typeNameAsString<Vec3f>()) ||
(actualValueType == typeNameAsString<double>() &&
requestedValueType == typeNameAsString<Vec3d>()) ||
(actualValueType == typeNameAsString<int32_t>() &&
requestedValueType == typeNameAsString<Vec3I>()));
if (!packVector && actualValueType != requestedValueType) {
OPENVDB_THROW(TypeError,
"Attribute type requested for rasterization \"" << requestedValueType << "\""
" must match attribute value type \"" << actualValueType << "\"");
}
}
tree::LeafManager<PointDataTreeT> leafManager(mGrid->tree());
// de-allocate voxel buffers if the points are being streamed and the caches are being released
const bool dropBuffers = mStream && reduceMemory;
// turn RasterGroups into a MultiGroupFilter for this VDB Grid (if applicable)
using ResolvedFilterT = typename point_rasterize_internal::RasterGroupTraits<FilterT>::NewFilterT;
auto resolvedFilter = point_rasterize_internal::RasterGroupTraits<FilterT>::resolve(
groupFilter, attributeSet);
// generate leaf offsets (if necessary)
if (mLeafOffsets.empty()) {
openvdb::points::pointOffsets(mLeafOffsets, mGrid->constTree(), resolvedFilter,
/*inCoreOnly=*/false, mSettings.threaded);
}
// set streaming arbitrary attribute array flags
if (mStream) {
if (attributeExists && !isPositionAttribute && !isVelocityAttribute && !isRadiusAttribute) {
leafManager.foreach(
[&](PointDataLeafT& leaf, size_t /*idx*/) {
leaf.attributeArray(attributeIndex).setStreaming(true);
},
mSettings.threaded);
}
if (reduceMemory) {
size_t positionIndex = attributeSet.find("P");
leafManager.foreach(
[&](PointDataLeafT& leaf, size_t /*idx*/) {
leaf.attributeArray(positionIndex).setStreaming(true);
},
mSettings.threaded);
if (mSettings.velocityMotionBlur || !mSettings.camera.isStatic()) {
size_t velocityIndex = attributeSet.find(velocityAttribute);
if (velocityIndex != points::AttributeSet::INVALID_POS) {
leafManager.foreach(
[&](PointDataLeafT& leaf, size_t /*idx*/) {
leaf.attributeArray(velocityIndex).setStreaming(true);
},
mSettings.threaded);
}
}
if (mSettings.useRadius) {
size_t radiusIndex = attributeSet.find(radiusAttribute);
if (radiusIndex != points::AttributeSet::INVALID_POS) {
leafManager.foreach(
[&](PointDataLeafT& leaf, size_t /*idx*/) {
leaf.attributeArray(radiusIndex).setStreaming(true);
},
mSettings.threaded);
}
}
}
}
const bool alignedTransform = *(mSettings.transform) == mGrid->constTransform();
RasterizeOp<PointDataGridT, AttributeT, GridT, ResolvedFilterT> rasterizeOp(
*mGrid, mLeafOffsets, attributeIndex, velocityAttribute, radiusAttribute, combiner, weightCombiner,
dropBuffers, scale, resolvedFilter, computeMax, alignedTransform, mSettings.camera.isStatic(),
mSettings, mMask, interrupter);
leafManager.foreach(rasterizeOp, mSettings.threaded);
// clean up leaf offsets (if reduce memory is enabled)
if (reduceMemory && !mLeafOffsets.empty()) {
mLeafOffsets.clear();
// de-allocate the vector data to ensure we keep memory footprint as low as possible
mLeafOffsets.shrink_to_fit();
}
}
private:
GridPtr mGrid;
const bool mStream;
const FrustumRasterizerSettings& mSettings;
const FrustumRasterizerMask& mMask;
int mLeafPercentage = -1;
std::vector<Index64> mLeafOffsets;
}; // class GridToRasterize
template <typename ValueT>
typename std::enable_if<!ValueTraits<ValueT>::IsVec, ValueT>::type
computeWeightedValue(const ValueT& value, const ValueT& weight)
{
constexpr bool isSignedInt = std::is_integral<ValueT>() && std::is_signed<ValueT>();
if (!math::isFinite(weight) || math::isApproxZero(weight) ||
(isSignedInt && math::isNegative(weight))) {
return zeroVal<ValueT>();
} else {
return value / weight;
}
}
template <typename ValueT>
typename std::enable_if<ValueTraits<ValueT>::IsVec, ValueT>::type
computeWeightedValue(const ValueT& value, const ValueT& weight)
{
using ElementT = typename ValueTraits<ValueT>::ElementType;
constexpr bool isSignedInt = std::is_integral<ElementT>() && std::is_signed<ElementT>();
ValueT result(value);
for (int i=0; i<ValueTraits<ValueT>::Size; ++i) {
if (!math::isFinite(weight[i]) || math::isApproxZero(weight[i]) ||
(isSignedInt && math::isNegative(weight[i]))) {
result[i] = zeroVal<ElementT>();
} else {
result[i] = value[i] / weight[i];
}
}
return result;
}
} // namespace point_rasterize_internal
/// @endcond
////////////////////////////////////////////////////////////////////////////
RasterCamera::RasterCamera(const math::Transform& transform)
: mTransforms{transform}
, mWeights{1} { }
bool RasterCamera::isStatic() const
{
return mTransforms.size() <= 1;
}
void RasterCamera::clear()
{
mTransforms.clear();
mWeights.clear();
}
void RasterCamera::appendTransform(const math::Transform& transform, float weight)
{
mTransforms.push_back(transform);
mWeights.push_back(weight);
}
size_t RasterCamera::size() const
{
return mTransforms.size();
}
void RasterCamera::simplify()
{
// if two or more identical transforms, only keep one
if (mTransforms.size() >= 2) {
const auto& transform = mTransforms.front();
bool isStatic = true;
for (const auto& testTransform : mTransforms) {
if (transform != testTransform) {
isStatic = false;
}
}
if (isStatic) {
while (mTransforms.size() > 1) {
mTransforms.pop_back();
}
}
}
// if all weights are equal to one, delete the weights array
if (!mWeights.empty()) {
bool hasWeight = false;
for (Index i = 0; i < mWeights.size(); i++) {
if (this->hasWeight(i)) {
hasWeight = true;
break;
}
}
if (!hasWeight) mWeights.clear();
}
}
bool RasterCamera::hasWeight(Index i) const
{
if (mWeights.empty()) return false;
assert(i < mWeights.size());
return !openvdb::math::isApproxEqual(mWeights[i], 1.0f, 1e-3f);
}
float RasterCamera::weight(Index i) const
{
if (mWeights.empty()) {
return 1.0f;
} else {
assert(i < mWeights.size());
return mWeights[i];
}
}
const math::Transform& RasterCamera::transform(Index i) const
{
if (mTransforms.size() == 1) {
return mTransforms.front();
} else {
assert(i < mTransforms.size());
return mTransforms[i];
}
}
const math::Transform& RasterCamera::firstTransform() const
{
assert(!mTransforms.empty());
return mTransforms.front();
}
const math::Transform& RasterCamera::lastTransform() const
{
assert(!mTransforms.empty());
return mTransforms.back();
}
void RasterCamera::setShutter(float start, float end)
{
mShutterStart = start;
mShutterEnd = end;
}
float RasterCamera::shutterStart() const
{
return mShutterStart;
}
float RasterCamera::shutterEnd() const
{
return mShutterEnd;
}
////////////////////////////////////////////////////////////////////////////
FrustumRasterizerMask::FrustumRasterizerMask( const math::Transform& transform,
const MaskGrid* mask,
const BBoxd& bbox,
const bool clipToFrustum,
const bool invertMask)
: mMask()
, mClipBBox()
, mInvert(invertMask)
{
// TODO: the current OpenVDB implementation for resampling masks is particularly slow,
// this is primarily because it uses a scatter reduction-style method that only samples
// and generates leaf nodes and relies on pruning to generate tiles, this could be
// significantly improved!
// convert world-space clip mask to index-space
if (mask) {
// resample mask to index space
mMask = MaskGrid::create();
mMask->setTransform(transform.copy());
tools::resampleToMatch<tools::PointSampler>(*mask, *mMask);
// prune the clip mask
tools::prune(mMask->tree());
}
// convert world-space clip bbox to index-space
if (!bbox.empty()) {
// create world-space mask (with identity linear transform)
MaskGrid::Ptr tempMask = MaskGrid::create();
CoordBBox coordBBox(Coord::floor(bbox.min()),
Coord::ceil(bbox.max()));
tempMask->sparseFill(coordBBox, true, true);
// resample mask to index space
MaskGrid::Ptr bboxMask = MaskGrid::create();
bboxMask->setTransform(mMask ? mMask->transformPtr() : transform.copy());
tools::resampleToMatch<tools::PointSampler>(*tempMask, *bboxMask);
// replace or union the mask
if (mMask) {
mMask->topologyUnion(*bboxMask);
} else {
mMask = bboxMask;
}
}
if (clipToFrustum) {
auto frustumMap = transform.template constMap<math::NonlinearFrustumMap>();
if (frustumMap) {
const auto& frustumBBox = frustumMap->getBBox();
mClipBBox.reset(Coord::floor(frustumBBox.min()),
Coord::ceil(frustumBBox.max()));
}
}
}
FrustumRasterizerMask::operator bool() const
{
return mMask || !mClipBBox.empty();
}
MaskTree::ConstPtr
FrustumRasterizerMask::getTreePtr() const
{
return mMask ? mMask->treePtr() : MaskTree::ConstPtr();
}
const CoordBBox&
FrustumRasterizerMask::clipBBox() const
{
return mClipBBox;
}
bool
FrustumRasterizerMask::valid(const Coord& ijk, const tree::ValueAccessor<const MaskTree>* acc) const
{
const bool maskValue = acc ? acc->isValueOn(ijk) : true;
const bool insideMask = mInvert ? !maskValue : maskValue;
const bool insideFrustum = mClipBBox.empty() ? true : mClipBBox.isInside(ijk);
return insideMask && insideFrustum;
}
////////////////////////////////////////////////////////////////////////////
template <typename PointDataGridT>
FrustumRasterizer<PointDataGridT>::FrustumRasterizer(const FrustumRasterizerSettings& settings,
const FrustumRasterizerMask& mask,
util::NullInterrupter* interrupt)
: mSettings(settings)
, mMask(mask)
, mInterrupter(interrupt)
{
if (mSettings.velocityAttribute.empty() && mSettings.velocityMotionBlur) {
OPENVDB_THROW(ValueError,
"Using velocity motion blur during rasterization requires a velocity attribute.");
}
}
template <typename PointDataGridT>
void
FrustumRasterizer<PointDataGridT>::addPoints(GridConstPtr& grid)
{
// skip any empty grids
if (!grid || grid->tree().empty()) return;
// note that streaming is not possible with a const grid
mPointGrids.emplace_back(grid, mSettings, mMask);
}
template <typename PointDataGridT>
void
FrustumRasterizer<PointDataGridT>::addPoints(GridPtr& grid, bool stream)
{
// skip any empty grids
if (!grid || grid->tree().empty()) return;
mPointGrids.emplace_back(grid, stream, mSettings, mMask);
}
template <typename PointDataGridT>
void
FrustumRasterizer<PointDataGridT>::clear()
{
mPointGrids.clear();
}
template <typename PointDataGridT>
size_t
FrustumRasterizer<PointDataGridT>::size() const
{
return mPointGrids.size();
}
template <typename PointDataGridT>
size_t
FrustumRasterizer<PointDataGridT>::memUsage() const
{
size_t mem = sizeof(*this) + sizeof(mPointGrids);
for (const auto& grid : mPointGrids) {
mem += grid.memUsage();
}
return mem;
}
template <typename PointDataGridT>
template <typename FilterT>
FloatGrid::Ptr
FrustumRasterizer<PointDataGridT>::rasterizeUniformDensity(
RasterMode mode, bool reduceMemory, float scale, const FilterT& filter)
{
// no attribute to rasterize, so just provide an empty string and default to float type
auto density = rasterizeAttribute<FloatGrid, float>("", mode, reduceMemory, scale, filter);
// hard-code grid name to density
density->setName("density");
return density;
}
template <typename PointDataGridT>
template <typename FilterT>
FloatGrid::Ptr
FrustumRasterizer<PointDataGridT>::rasterizeDensity(
const openvdb::Name& attribute, RasterMode mode, bool reduceMemory, float scale, const FilterT& filter)
{
auto density = rasterizeAttribute<FloatGrid, float>(attribute, mode, reduceMemory, scale, filter);
// hard-code grid name to density
density->setName("density");
return density;
}
template <typename PointDataGridT>
template <typename FilterT>
GridBase::Ptr
FrustumRasterizer<PointDataGridT>::rasterizeAttribute(
const Name& attribute, RasterMode mode, bool reduceMemory, float scale, const FilterT& filter)
{
// retrieve the source type of the attribute
Name sourceType;
Index stride(0);
for (const auto& points : mPointGrids) {
auto leaf = points.tree().cbeginLeaf();
const auto& descriptor = leaf->attributeSet().descriptor();
const size_t targetIndex = descriptor.find(attribute);
// ignore grids which don't have the attribute
if (targetIndex == points::AttributeSet::INVALID_POS) continue;
const auto* attributeArray = leaf->attributeSet().getConst(attribute);
if (!attributeArray) continue;
stride = attributeArray->stride();
sourceType = descriptor.valueType(targetIndex);
if (!sourceType.empty()) break;
}
// no valid point attributes for rasterization
// TODO: add a warning / error in the case that there are non-zero grids
if (sourceType.empty()) return {};
if (stride == 1 && sourceType == typeNameAsString<float>()) {
using GridT = typename PointDataGridT::template ValueConverter<float>::Type;
return rasterizeAttribute<GridT, float>(attribute, mode, reduceMemory, scale, filter);
// this define is for lowering compilation time when debugging by instantiating only the float
// code path (default is to instantiate all code paths)
#ifndef ONLY_RASTER_FLOAT
} else if ( sourceType == typeNameAsString<Vec3f>() ||
(stride == 3 && sourceType == typeNameAsString<float>())) {
using GridT = typename PointDataGridT::template ValueConverter<Vec3f>::Type;
return rasterizeAttribute<GridT, Vec3f>(attribute, mode, reduceMemory, scale, filter);
} else if ( sourceType == typeNameAsString<Vec3d>() ||
(stride == 3 && sourceType == typeNameAsString<double>())) {
using GridT = typename PointDataGridT::template ValueConverter<Vec3d>::Type;
return rasterizeAttribute<GridT, Vec3d>(attribute, mode, reduceMemory, scale, filter);
} else if ( sourceType == typeNameAsString<Vec3i>() ||
(stride == 3 && sourceType == typeNameAsString<int32_t>())) {
using GridT = typename PointDataGridT::template ValueConverter<Vec3i>::Type;
return rasterizeAttribute<GridT, Vec3i>(attribute, mode, reduceMemory, scale, filter);
} else if (stride == 1 && sourceType == typeNameAsString<int16_t>()) {
using GridT = typename PointDataGridT::template ValueConverter<Int32>::Type;
return rasterizeAttribute<GridT, int16_t>(attribute, mode, reduceMemory, scale, filter);
} else if (stride == 1 && sourceType == typeNameAsString<int32_t>()) {
using GridT = typename PointDataGridT::template ValueConverter<Int32>::Type;
return rasterizeAttribute<GridT, int32_t>(attribute, mode, reduceMemory, scale, filter);
} else if (stride == 1 && sourceType == typeNameAsString<int64_t>()) {
using GridT = typename PointDataGridT::template ValueConverter<Int64>::Type;
return rasterizeAttribute<GridT, int64_t>(attribute, mode, reduceMemory, scale, filter);
} else if (stride == 1 && sourceType == typeNameAsString<double>()) {
using GridT = typename PointDataGridT::template ValueConverter<double>::Type;
return rasterizeAttribute<GridT, double>(attribute, mode, reduceMemory, scale, filter);
} else if (stride == 1 && sourceType == typeNameAsString<bool>()) {
using GridT = typename PointDataGridT::template ValueConverter<bool>::Type;
return rasterizeAttribute<GridT, bool>(attribute, mode, reduceMemory, true, filter);
#endif
} else {
std::ostringstream ostr;
ostr << "Cannot rasterize attribute of type - " << sourceType;
if (stride > 1) ostr << " x " << stride;
OPENVDB_THROW(TypeError, ostr.str());
}
}
template <typename PointDataGridT>
template <typename GridT, typename AttributeT, typename FilterT>
typename GridT::Ptr
FrustumRasterizer<PointDataGridT>::rasterizeAttribute(const Name& attribute, RasterMode mode,
bool reduceMemory, float scale, const FilterT& filter)
{
if (attribute == "P") {
OPENVDB_THROW(ValueError, "Cannot rasterize position attribute.")
}
auto grid = GridT::create();
grid->setName(attribute);
grid->setTransform(mSettings.transform->copy());
this->performRasterization<AttributeT>(*grid, mode, attribute, reduceMemory, scale, filter);
return grid;
}
template <typename PointDataGridT>
template <typename GridT, typename FilterT>
typename GridT::Ptr
FrustumRasterizer<PointDataGridT>::rasterizeMask(bool reduceMemory, const FilterT& filter)
{
using ValueT = typename GridT::ValueType;
static_assert(point_rasterize_internal::BoolTraits<ValueT>::IsBool,
"Value type of mask to be rasterized must be bool or ValueMask.");
auto grid = rasterizeAttribute<GridT, ValueT>("", RasterMode::ACCUMULATE, reduceMemory, true, filter);
grid->setName("mask");
return grid;
}
template <typename PointDataGridT>
template <typename AttributeT, typename GridT, typename FilterT>
void
FrustumRasterizer<PointDataGridT>::performRasterization(
GridT& grid, RasterMode mode, const openvdb::Name& attribute, bool reduceMemory,
float scale, const FilterT& filter)
{
using openvdb::points::point_mask_internal::GridCombinerOp;
using point_rasterize_internal::computeWeightedValue;
using TreeT = typename GridT::TreeType;
using LeafT = typename TreeT::LeafNodeType;
using ValueT = typename TreeT::ValueType;
using CombinerOpT = typename point_rasterize_internal::CombinableTraits<GridT>::OpT;
using CombinableT = typename point_rasterize_internal::CombinableTraits<GridT>::T;
// start interrupter with a descriptive name
if (mInterrupter) {
std::stringstream ss;
ss << "Rasterizing Points ";
if (!mSettings.camera.isStatic() || mSettings.velocityMotionBlur) {
ss << "with Motion Blur ";
}
if (mSettings.transform->isLinear()) {
ss << "to Linear Volume";
} else {
ss << "to Frustum Volume";
}
mInterrupter->start(ss.str().c_str());
}
const bool useMaximum = mode == RasterMode::MAXIMUM;
const bool useWeight = mode == RasterMode::AVERAGE;
if (useMaximum && VecTraits<ValueT>::IsVec) {
OPENVDB_THROW(ValueError,
"Cannot use maximum mode when rasterizing vector attributes.");
}
if ((useMaximum || useWeight) && point_rasterize_internal::BoolTraits<ValueT>::IsBool) {
OPENVDB_THROW(ValueError,
"Cannot use maximum or average modes when rasterizing bool attributes.");
}
CombinableT combiner;
CombinableT weightCombiner;
CombinableT* weightCombinerPtr = useWeight ? &weightCombiner : nullptr;
// use leaf count as an approximate indicator for progress as it
// doesn't even require loading the topology
if (mInterrupter) {
if (mPointGrids.size() == 1) {
mPointGrids[0].setLeafPercentage(100);
}
else {
// compute cumulative leaf counts per grid
Index64 leafCount(0);
std::vector<Index64> leafCounts;
leafCounts.reserve(mPointGrids.size());
for (auto& points : mPointGrids) {
leafCount += points.tree().leafCount();
leafCounts.push_back(leafCount);
}
// avoid dividing by zero
if (leafCount == Index64(0)) leafCount++;
// compute grid percentages based on leaf count
for (size_t i = 0; i < leafCounts.size(); i++) {
int percentage = static_cast<int>(math::Round((static_cast<float>(leafCounts[i]))/
static_cast<float>(leafCount)));
mPointGrids[i].setLeafPercentage(percentage);
}
}
}
// rasterize each point grid into each grid in turn
for (auto& points : mPointGrids) {
points.template rasterize<AttributeT, GridT>(
attribute, combiner, weightCombinerPtr, scale, filter, mode == RasterMode::MAXIMUM, reduceMemory, mInterrupter);
// interrupt if requested and update the progress percentage
// note that even when interrupting, the operation to combine the local grids
// is completed so that the user receives a partially rasterized volume
if (mInterrupter &&
mInterrupter->wasInterrupted(points.leafPercentage())) {
break;
}
}
// combine the value grids into one
CombinerOpT combineOp(grid, mode != RasterMode::MAXIMUM);
combiner.combine_each(combineOp);
if (useWeight) {
// combine the weight grids into one
auto weightGrid = GridT::create(ValueT(1));
CombinerOpT weightCombineOp(*weightGrid, /*sum=*/true);
weightCombiner.combine_each(weightCombineOp);
tree::LeafManager<TreeT> leafManager(grid.tree());
leafManager.foreach(
[&](LeafT& leaf, size_t /*idx*/) {
auto weightAccessor = weightGrid->getConstAccessor();
for (auto iter = leaf.beginValueOn(); iter; ++iter) {
auto weight = weightAccessor.getValue(iter.getCoord());
iter.setValue(computeWeightedValue(iter.getValue(), weight));
}
},
mSettings.threaded);
}
if (mInterrupter) mInterrupter->end();
}
} // namespace points
} // namespace OPENVDB_VERSION_NAME
} // namespace openvdb
#endif // OPENVDB_POINTS_POINT_RASTERIZE_FRUSTUM_IMPL_HAS_BEEN_INCLUDED
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
openvdb::v11_0::Coord::ceil
static Coord ceil(const Vec3< T > &xyz)
Return the largest integer coordinates that are not greater than xyz+1 (node centered conversion).
Definition Coord.h:63
openvdb::v11_0::Coord::floor
static Coord floor(const Vec3< T > &xyz)
Return the largest integer coordinates that are not greater than xyz (node centered conversion).
Definition Coord.h:56
openvdb::v11_0::GridBase::Ptr
SharedPtr< GridBase > Ptr
Definition Grid.h:80
openvdb::v11_0::Grid< FloatTree >::Ptr
SharedPtr< Grid > Ptr
Definition Grid.h:573
openvdb::v11_0::Grid< MaskTree >::create
static Ptr create()
openvdb::v11_0::TypeError
Definition Exceptions.h:64
openvdb::v11_0::ValueError
Definition Exceptions.h:65
openvdb::v11_0::math::BBox::empty
bool empty() const
Return true if this bounding box is empty, i.e., it has no (positive) volume.
Definition BBox.h:196
openvdb::v11_0::math::CoordBBox
Axis-aligned bounding box of signed integer coordinates.
Definition Coord.h:251
openvdb::v11_0::math::Coord
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:25
openvdb::v11_0::math::Transform
Definition Transform.h:40
openvdb::v11_0::math::Transform::copy
Ptr copy() const
Definition Transform.h:50
openvdb::v11_0::math::Vec3::x
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition Vec3.h:85
openvdb::v11_0::math::Vec3::length
T length() const
Length of the vector.
Definition Vec3.h:200
openvdb::v11_0::math::Vec3::dot
T dot(const Vec3< T > &v) const
Dot product.
Definition Vec3.h:191
openvdb::v11_0::points::AttributeSet::INVALID_POS
@ INVALID_POS
Definition AttributeSet.h:41
openvdb::v11_0::points::FrustumRasterizer::GridConstPtr
typename PointDataGridT::ConstPtr GridConstPtr
Definition PointRasterizeFrustum.h:167
openvdb::v11_0::points::FrustumRasterizer::memUsage
size_t memUsage() const
Return memory usage of the rasterizer.
Definition PointRasterizeFrustumImpl.h:1315
openvdb::v11_0::points::FrustumRasterizer::size
size_t size() const
Return number of PointDataGrids in the rasterizer.
Definition PointRasterizeFrustumImpl.h:1308
openvdb::v11_0::points::FrustumRasterizer::FrustumRasterizer
FrustumRasterizer(const FrustumRasterizerSettings &settings, const FrustumRasterizerMask &mask=FrustumRasterizerMask(), util::NullInterrupter *interrupt=nullptr)
main constructor
Definition PointRasterizeFrustumImpl.h:1265
openvdb::v11_0::points::FrustumRasterizer::rasterizeDensity
FloatGrid::Ptr rasterizeDensity(const openvdb::Name &attribute, RasterMode mode=RasterMode::MAXIMUM, bool reduceMemory=false, float scale=1.0f, const FilterT &filter=FilterT())
Definition PointRasterizeFrustumImpl.h:1340
openvdb::v11_0::points::FrustumRasterizer::addPoints
void addPoints(GridConstPtr &points)
Append a PointDataGrid to the rasterizer (but don't rasterize yet).
Definition PointRasterizeFrustumImpl.h:1280
openvdb::v11_0::points::FrustumRasterizer::rasterizeMask
GridT::Ptr rasterizeMask(bool reduceMemory=false, const FilterT &filter=FilterT())
Definition PointRasterizeFrustumImpl.h:1440
openvdb::v11_0::points::FrustumRasterizer::GridPtr
typename PointDataGridT::Ptr GridPtr
Definition PointRasterizeFrustum.h:166
openvdb::v11_0::points::FrustumRasterizer::clear
void clear()
Clear all PointDataGrids in the rasterizer.
Definition PointRasterizeFrustumImpl.h:1301
openvdb::v11_0::points::FrustumRasterizer::rasterizeAttribute
GridBase::Ptr rasterizeAttribute(const Name &attribute, RasterMode mode=RasterMode::ACCUMULATE, bool reduceMemory=false, float scale=1.0f, const FilterT &filter=FilterT())
Definition PointRasterizeFrustumImpl.h:1352
openvdb::v11_0::points::FrustumRasterizer::rasterizeUniformDensity
FloatGrid::Ptr rasterizeUniformDensity(RasterMode mode=RasterMode::MAXIMUM, bool reduceMemory=false, float scale=1.0f, const FilterT &filter=FilterT())
Definition PointRasterizeFrustumImpl.h:1327
openvdb::v11_0::points::RasterCamera::weight
float weight(Index i) const
Definition PointRasterizeFrustumImpl.h:1126
openvdb::v11_0::points::RasterCamera::simplify
void simplify()
Definition PointRasterizeFrustumImpl.h:1089
openvdb::v11_0::points::RasterCamera::transform
const math::Transform & transform(Index i) const
Definition PointRasterizeFrustumImpl.h:1136
openvdb::v11_0::points::RasterCamera::size
size_t size() const
Definition PointRasterizeFrustumImpl.h:1084
openvdb::v11_0::points::RasterCamera::firstTransform
const math::Transform & firstTransform() const
Definition PointRasterizeFrustumImpl.h:1146
openvdb::v11_0::points::RasterCamera::isStatic
bool isStatic() const
Definition PointRasterizeFrustumImpl.h:1067
openvdb::v11_0::points::RasterCamera::setShutter
void setShutter(float start, float end)
Definition PointRasterizeFrustumImpl.h:1158
openvdb::v11_0::points::RasterCamera::appendTransform
void appendTransform(const math::Transform &, float weight=1.0f)
Definition PointRasterizeFrustumImpl.h:1078
openvdb::v11_0::points::RasterCamera::shutterEnd
float shutterEnd() const
Definition PointRasterizeFrustumImpl.h:1169
openvdb::v11_0::points::RasterCamera::hasWeight
bool hasWeight(Index i) const
Definition PointRasterizeFrustumImpl.h:1119
openvdb::v11_0::points::RasterCamera::RasterCamera
RasterCamera(const math::Transform &transform)
Definition PointRasterizeFrustumImpl.h:1063
openvdb::v11_0::points::RasterCamera::clear
void clear()
Definition PointRasterizeFrustumImpl.h:1072
openvdb::v11_0::points::RasterCamera::lastTransform
const math::Transform & lastTransform() const
Definition PointRasterizeFrustumImpl.h:1152
openvdb::v11_0::points::RasterCamera::shutterStart
float shutterStart() const
Definition PointRasterizeFrustumImpl.h:1164
openvdb::v11_0::tree::Tree::ConstPtr
SharedPtr< const Tree > ConstPtr
Definition Tree.h:181
openvdb::v11_0::tree::ValueAccessorImpl::isValueOn
bool isValueOn(const Coord &xyz) const
Return the active state of the voxel at the given coordinates.
Definition ValueAccessor.h:480
nanovdb::CoordBBox
BBox< Coord > CoordBBox
Definition NanoVDB.h:2535
openvdb::v11_0::math::Vec2s
Vec2< float > Vec2s
Definition Vec2.h:532
openvdb::v11_0::math::scale
MatType scale(const Vec3< typename MatType::value_type > &s)
Return a matrix that scales by s.
Definition Mat.h:615
openvdb::v11_0::math::Vec3d
Vec3< double > Vec3d
Definition Vec3.h:664
openvdb::v11_0::math::Round
float Round(float x)
Return x rounded to the nearest integer.
Definition Math.h:819
openvdb::v11_0::math::Vec3i
Vec3< int32_t > Vec3i
Definition Vec3.h:661
openvdb::v11_0::points
Definition AttributeArray.h:41
openvdb::v11_0::points::rasterize
void rasterize(const PointDataTreeOrGridT &points, TransferT &transfer, const FilterT &filter=NullFilter(), InterrupterT *interrupter=nullptr)
Perform potentially complex rasterization from a user defined transfer scheme.
Definition PointTransfer.h:556
openvdb::v11_0::points::RasterMode
RasterMode
How to composite points into a volume.
Definition PointRasterizeFrustum.h:30
openvdb::v11_0::points::RasterMode::MAXIMUM
@ MAXIMUM
Definition PointRasterizeFrustum.h:32
openvdb::v11_0::points::RasterMode::AVERAGE
@ AVERAGE
Definition PointRasterizeFrustum.h:33
openvdb::v11_0::points::RasterMode::ACCUMULATE
@ ACCUMULATE
Definition PointRasterizeFrustum.h:31
openvdb::v11_0::tools::prune
void prune(TreeT &tree, typename TreeT::ValueType tolerance=zeroVal< typename TreeT::ValueType >(), bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing with tiles any nodes whose values are all the same...
Definition Prune.h:335
openvdb::v11_0::tools::memUsage
Index64 memUsage(const TreeT &tree, bool threaded=true)
Return the total amount of memory in bytes occupied by this tree.
Definition Count.h:493
openvdb::v11_0::tools::resampleToMatch
void resampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
Resample an input grid into an output grid of the same type such that, after resampling,...
Definition GridTransformer.h:486
openvdb::v11_0::tree::ValueAccessor
ValueAccessorImpl< TreeType, IsSafe, MutexType, openvdb::make_index_sequence< CacheLevels > > ValueAccessor
Default alias for a ValueAccessor. This is simply a helper alias for the generic definition but takes...
Definition ValueAccessor.h:86
openvdb::v11_0::Name
std::string Name
Definition Name.h:19
openvdb::v11_0::Index
Index32 Index
Definition Types.h:54
openvdb::v11_0::Vec3f
math::Vec3< float > Vec3f
Definition Types.h:74
openvdb::v11_0::ConstPtrCast
SharedPtr< T > ConstPtrCast(const SharedPtr< U > &ptr)
Return a new shared pointer that points to the same object as the given pointer but with possibly dif...
Definition Types.h:126
openvdb::v11_0::BBoxd
math::BBox< Vec3d > BBoxd
Definition Types.h:84
openvdb::v11_0::MaskGrid
Grid< MaskTree > MaskGrid
Definition openvdb.h:78
openvdb::v11_0::Vec3R
math::Vec3< Real > Vec3R
Definition Types.h:72
openvdb::v11_0::Index64
uint64_t Index64
Definition Types.h:53
openvdb::v11_0::typeNameAsString
const char * typeNameAsString()
Definition Types.h:516
openvdb
Definition Exceptions.h:13
openvdb::v11_0::VecTraits::IsVec
static const bool IsVec
Definition Types.h:245
openvdb::v11_0::points::FrustumRasterizerMask
Definition PointRasterizeFrustum.h:112
openvdb::v11_0::points::FrustumRasterizerMask::FrustumRasterizerMask
FrustumRasterizerMask()=default
openvdb::v11_0::points::FrustumRasterizerMask::valid
bool valid(const Coord &ijk, AccessorT *acc) const
Definition PointRasterizeFrustumImpl.h:1252
openvdb::v11_0::points::FrustumRasterizerMask::getTreePtr
MaskTree::ConstPtr getTreePtr() const
Definition PointRasterizeFrustumImpl.h:1240
openvdb::v11_0::points::FrustumRasterizerMask::clipBBox
const CoordBBox & clipBBox() const
Definition PointRasterizeFrustumImpl.h:1246
openvdb::v11_0::points::FrustumRasterizerSettings
A group of shared settings to be used in the Volume Rasterizer.
Definition PointRasterizeFrustum.h:87
openvdb::v11_0::util::NullInterrupter
Base class for interrupters.
Definition NullInterrupter.h:26
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:212