Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

### Features

- [usd#2632](https://github.com/Autodesk/arnold-usd/issues/2632) - Support holeIndices in UsdGeomMesh for non-subdivided meshes
- [usd#2645](https://github.com/Autodesk/arnold-usd/issues/2645) - Support ArnoldNodeGraph primitives when the usd files are referenced
- [usd#2619](https://github.com/Autodesk/arnold-usd/issues/2619) - Add MtoA scene index plugin for MayaHydra support of custom attributes
- [usd#2583](https://github.com/Autodesk/arnold-usd/issues/2583) - Support nested instancers with lightweight shape instancing
Expand Down
100 changes: 100 additions & 0 deletions libs/common/shape_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,4 +331,104 @@ bool FlattenIndexedValue(const VtValue& in, const VtIntArray& idx, VtValue& out)
TfToken, GfHalf, GfVec2h, GfVec3h, GfVec4h, GfMatrix4f, GfMatrix4d>(in, idx, out);

}

// ---------------------------------------------------------------------------
// MeshHoleFilter
// ---------------------------------------------------------------------------

void MeshHoleFilter::Clear()
{
_isHole.clear();
_offsets.clear();
_holeCount = 0;
}

void MeshHoleFilter::Build(const VtIntArray& holeIndices, const VtIntArray& originalFaceVertexCounts)
{
Clear();
if (holeIndices.empty() || originalFaceVertexCounts.empty())
return;

const size_t numFaces = originalFaceVertexCounts.size();
_isHole.assign(numFaces, false);
for (int idx : holeIndices) {
if (idx >= 0 && static_cast<size_t>(idx) < numFaces && !_isHole[idx]) {
_isHole[idx] = true;
++_holeCount;
}
}
if (_holeCount == 0) {
_isHole.clear();
return;
}

// Prefix sum of face vertex counts so face-varying ranges can be copied directly.
_offsets.resize(numFaces + 1);
_offsets[0] = 0;
for (size_t i = 0; i < numFaces; ++i) {
const int count = originalFaceVertexCounts[i];
_offsets[i + 1] = _offsets[i] + (count > 0 ? static_cast<size_t>(count) : 0);
}
}

namespace {

template <typename T>
inline bool _TryFilterUniform(const MeshHoleFilter& f, VtValue& value)
{
if (!value.IsHolding<VtArray<T>>())
return false;
VtArray<T> arr = value.UncheckedGet<VtArray<T>>();
if (f.FilterUniformArray(arr))
value = VtValue(std::move(arr));
return true;
}

template <typename T>
inline bool _TryFilterFaceVarying(const MeshHoleFilter& f, VtValue& value)
{
if (!value.IsHolding<VtArray<T>>())
return false;
VtArray<T> arr = value.UncheckedGet<VtArray<T>>();
if (f.FilterFaceVaryingArray(arr))
value = VtValue(std::move(arr));
return true;
}

} // namespace

bool MeshHoleFilter::FilterUniformValue(VtValue& value) const
{
if (Empty())
return false;
return _TryFilterUniform<float>(*this, value) ||
_TryFilterUniform<double>(*this, value) ||
_TryFilterUniform<int>(*this, value) ||
_TryFilterUniform<bool>(*this, value) ||
_TryFilterUniform<GfVec2f>(*this, value) ||
_TryFilterUniform<GfVec3f>(*this, value) ||
_TryFilterUniform<GfVec4f>(*this, value) ||
_TryFilterUniform<GfVec2d>(*this, value) ||
_TryFilterUniform<GfVec3d>(*this, value) ||
_TryFilterUniform<TfToken>(*this, value) ||
_TryFilterUniform<std::string>(*this, value);
}

bool MeshHoleFilter::FilterFaceVaryingValue(VtValue& value) const
{
if (Empty())
return false;
return _TryFilterFaceVarying<float>(*this, value) ||
_TryFilterFaceVarying<double>(*this, value) ||
_TryFilterFaceVarying<int>(*this, value) ||
_TryFilterFaceVarying<bool>(*this, value) ||
_TryFilterFaceVarying<GfVec2f>(*this, value) ||
_TryFilterFaceVarying<GfVec3f>(*this, value) ||
_TryFilterFaceVarying<GfVec4f>(*this, value) ||
_TryFilterFaceVarying<GfVec2d>(*this, value) ||
_TryFilterFaceVarying<GfVec3d>(*this, value) ||
_TryFilterFaceVarying<TfToken>(*this, value) ||
_TryFilterFaceVarying<std::string>(*this, value);
}

PXR_NAMESPACE_CLOSE_SCOPE
109 changes: 109 additions & 0 deletions libs/common/shape_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,113 @@ AtArray* GenerateVertexIdxs(const VtIntArray& indices, AtArray* vidxs);
/// Type to store arnold param names and values.
using ArnoldUsdParamValueList = std::vector<std::pair<AtString, VtValue>>;

/// Helper that filters mesh data to drop entries belonging to "hole" faces
/// (as expressed by USD's `holeIndices` attribute).
///
/// The class pre-computes:
/// - a `std::vector<bool>` membership table for O(1) hole-face tests, and
/// - a prefix sum of the original face-vertex counts so that face-varying
/// entries can be filtered with a single range copy per face.
///
/// All filter methods are no-ops (return false) when `Empty()` is true,
/// so callers can safely wrap them unconditionally.
class ARCH_HIDDEN MeshHoleFilter {
public:
MeshHoleFilter() = default;

/// Build / rebuild the internal lookup tables.
/// If @p holeIndices is empty, the filter becomes empty (no-op).
void Build(const VtIntArray& holeIndices, const VtIntArray& originalFaceVertexCounts);

/// Reset to the empty state.
void Clear();

bool Empty() const { return _holeCount == 0; }
size_t NumOriginalFaces() const { return _isHole.size(); }
size_t NumKeptFaces() const { return _isHole.size() - _holeCount; }
size_t NumHoles() const { return _holeCount; }
/// True if the face index is marked as a hole. Index must be < NumOriginalFaces().
bool IsHole(size_t face) const { return _isHole[face]; }

/// Filter a per-face (uniform) array (one entry per face).
/// Returns true if filtering was applied; false on no-op (filter empty
/// or array size does not match the original face count).
template <typename T>
bool FilterUniformArray(VtArray<T>& arr) const
{
if (Empty()) return false;
if (arr.size() != _isHole.size()) return false;
// Read via const cdata() to avoid COW detach on a possibly-shared
Comment on lines +263 to +271
// input; the input is then replaced via move-assign below.
const T* src = arr.cdata();
VtArray<T> out;
out.reserve(NumKeptFaces());
for (size_t i = 0; i < _isHole.size(); ++i) {
if (!_isHole[i]) out.push_back(src[i]);
}
arr = std::move(out);
return true;
}
template <typename T>
bool FilterUniformArray(std::vector<T>& arr) const
{
if (Empty()) return false;
if (arr.size() != _isHole.size()) return false;
std::vector<T> out;
out.reserve(NumKeptFaces());
for (size_t i = 0; i < _isHole.size(); ++i) {
if (!_isHole[i]) out.push_back(arr[i]);
}
arr = std::move(out);
return true;
}

/// Filter a face-varying array (entries grouped per face, sized to the
/// sum of the original face vertex counts). Uses range copies based on
/// the pre-computed prefix-sum offsets.
template <typename T>
bool FilterFaceVaryingArray(VtArray<T>& arr) const
{
if (Empty()) return false;
const size_t expected = _offsets.empty() ? 0 : _offsets.back();
if (arr.size() < expected) return false;
// cdata() does not trigger COW detach on the input.
const T* src = arr.cdata();
VtArray<T> out;
out.reserve(expected); // upper bound; actual is expected - holeVertexCount
for (size_t i = 0; i < _isHole.size(); ++i) {
if (_isHole[i]) continue;
out.insert(out.end(), src + _offsets[i], src + _offsets[i + 1]);
}
arr = std::move(out);
return true;
}
template <typename T>
bool FilterFaceVaryingArray(std::vector<T>& arr) const
{
if (Empty()) return false;
const size_t expected = _offsets.empty() ? 0 : _offsets.back();
if (arr.size() < expected) return false;
std::vector<T> out;
out.reserve(expected);
for (size_t i = 0; i < _isHole.size(); ++i) {
if (_isHole[i]) continue;
out.insert(out.end(), arr.begin() + _offsets[i], arr.begin() + _offsets[i + 1]);
}
arr = std::move(out);
return true;
}

/// Filter a uniform VtValue by trying the common primvar element types.
/// Returns true if a matching type was found and filtered.
bool FilterUniformValue(VtValue& value) const;
/// Filter a face-varying VtValue by trying the common primvar element types.
bool FilterFaceVaryingValue(VtValue& value) const;

private:
std::vector<bool> _isHole; ///< Per-face hole membership (size = numOriginalFaces).
std::vector<size_t> _offsets; ///< Prefix sum of face vertex counts (size = numOriginalFaces + 1).
size_t _holeCount = 0; ///< Number of unique hole faces.
};

PXR_NAMESPACE_CLOSE_SCOPE
Loading