3535#include " IECoreScene/MeshAlgo.h"
3636#include " IECoreScene/PrimitiveVariable.h"
3737
38+ #include " IECore/DataAlgo.h"
3839#include " IECore/PointDistribution.h"
3940#include " IECore/SimpleTypedData.h"
4041#include " IECore/TriangleAlgo.h"
42+ #include " IECore/TypeTraits.h"
4143
4244#include " tbb/blocked_range.h"
4345#include " tbb/parallel_for.h"
4446
47+ #include < any>
48+
4549using namespace Imath ;
4650using namespace IECore ;
4751using namespace IECoreScene ;
@@ -97,6 +101,7 @@ void triangleCornerPrimVarValues(
97101 default :
98102 // / Unimplemented primvar interpolation, or Constant, which doesn't support IndexedView
99103 assert ( false );
104+ a = b = c = T (0 .0f );
100105 }
101106}
102107
@@ -134,27 +139,47 @@ T triangleInterpolatedPrimVarValue(
134139void processInputs (
135140 const MeshPrimitive *mesh,
136141 const std::string &refPosition, const std::string &uvSet, const std::string &densityMask,
142+ const StringAlgo::MatchPattern &primitiveVariables,
137143 const Canceller *canceller,
138- MeshPrimitivePtr &triangulatedMesh ,
139- PrimitiveVariable &uvVar,
140- PrimitiveVariable &faceAreaVar, PrimitiveVariable &textureAreaVar, PrimitiveVariable &densityVar
144+ MeshPrimitivePtr &processedMesh ,
145+ PrimitiveVariable &uvVar, PrimitiveVariable &densityVar,
146+ PrimitiveVariable &faceAreaVar, PrimitiveVariable &textureAreaVar
141147)
142148{
143149 if ( !mesh )
144150 {
145151 throw InvalidArgumentException ( " MeshAlgo::distributePoints : The input mesh is not valid" );
146152 }
147153
148- // \todo - remove unused primvars
149- triangulatedMesh = MeshAlgo::triangulate ( mesh, canceller );
154+ IECoreScene::MeshPrimitivePtr meshWithUsedPrimVars = new MeshPrimitive ();
155+
156+ // We need the topolgy of the source mesh to triangulate it
157+ meshWithUsedPrimVars->setTopologyUnchecked ( mesh->verticesPerFace (), mesh->vertexIds (), mesh->variableSize ( PrimitiveVariable::Vertex ), mesh->interpolation () );
150158
151- if ( !triangulatedMesh || !triangulatedMesh->arePrimitiveVariablesValid () )
159+ // Note that we do not transfer creases or corners - they do not affect distribution of points. If we were
160+ // to add support for distributing onto the limit surface of a subdiv, then we might need to keep those ...
161+ // but that would need to happen on an untriangulated mesh anyway.
162+
163+ // Transfer the subset of variables that we need
164+ for ( auto var : mesh->variables )
165+ {
166+ if (
167+ var.first == uvSet || var.first == densityMask || var.first == refPosition || var.first == " P" ||
168+ StringAlgo::matchMultiple ( var.first , primitiveVariables )
169+ )
170+ {
171+ meshWithUsedPrimVars->variables [ var.first ] = var.second ;
172+ }
173+ }
174+
175+ processedMesh = MeshAlgo::triangulate ( meshWithUsedPrimVars.get (), canceller );
176+ if ( !processedMesh || !processedMesh->arePrimitiveVariablesValid () )
152177 {
153178 throw InvalidArgumentException ( " MeshAlgo::distributePoints : The input mesh could not be triangulated" );
154179 }
155180
156- auto uvIt = triangulatedMesh ->variables .find ( uvSet );
157- if ( uvIt != triangulatedMesh ->variables .end () )
181+ auto uvIt = processedMesh ->variables .find ( uvSet );
182+ if ( uvIt != processedMesh ->variables .end () )
158183 {
159184 PrimitiveVariable::Interpolation interp = uvIt->second .interpolation ;
160185 if (
@@ -177,15 +202,41 @@ void processInputs(
177202 throw InvalidArgumentException ( e );
178203 }
179204
180- faceAreaVar = MeshAlgo::calculateFaceArea ( triangulatedMesh.get (), refPosition, canceller );
181- textureAreaVar = MeshAlgo::calculateFaceTextureArea ( triangulatedMesh.get (), uvSet, refPosition, canceller );
205+ faceAreaVar = MeshAlgo::calculateFaceArea ( processedMesh.get (), refPosition, canceller );
206+ // It is ambiguous whether to pass "P" or refPosition here - the position argument of
207+ // calculateFaceTextureArea is not used for anything, and if it was used for something, I'm not sure what
208+ // it would be
209+ textureAreaVar = MeshAlgo::calculateFaceTextureArea ( processedMesh.get (), uvSet, refPosition, canceller );
182210
183- auto densityVarIt = triangulatedMesh->variables .find ( densityMask );
184- if ( densityVarIt != triangulatedMesh->variables .end () && ( densityVarIt->second .data ->typeId () == FloatVectorDataTypeId || densityVarIt->second .data ->typeId () == FloatDataTypeId ) )
211+ if ( !StringAlgo::matchMultiple ( uvSet, primitiveVariables ) )
185212 {
186- densityVar = densityVarIt-> second ;
213+ processedMesh-> variables . erase ( uvSet ) ;
187214 }
188- else
215+
216+ if (
217+ refPosition != " P" &&
218+ processedMesh->variables .find ( refPosition ) != processedMesh->variables .end () &&
219+ !StringAlgo::matchMultiple ( refPosition, primitiveVariables )
220+ )
221+ {
222+ processedMesh->variables .erase ( refPosition );
223+ }
224+
225+ auto densityVarIt = processedMesh->variables .find ( densityMask );
226+ if ( densityVarIt != processedMesh->variables .end () )
227+ {
228+ if ( densityVarIt->second .data ->typeId () == FloatVectorDataTypeId || densityVarIt->second .data ->typeId () == FloatDataTypeId )
229+ {
230+ densityVar = densityVarIt->second ;
231+ }
232+
233+ if ( !StringAlgo::matchMultiple ( densityMask, primitiveVariables ) )
234+ {
235+ processedMesh->variables .erase ( densityMask );
236+ }
237+ }
238+
239+ if ( !densityVar.data )
189240 {
190241 densityVar = PrimitiveVariable ( PrimitiveVariable::Constant, new FloatData ( 1.0 ) );
191242 }
@@ -254,22 +305,28 @@ void distributePointsInTriangle(
254305 );
255306}
256307
308+ template <typename T>
309+ constexpr bool supportsAddMult ()
310+ {
311+ return std::is_arithmetic_v<T> || TypeTraits::IsColor<T>::value || TypeTraits::IsVec<T>::value || TypeTraits::IsMatrix<T>::value;
312+ }
313+
257314} // namespace
258315
259- PointsPrimitivePtr MeshAlgo::distributePoints ( const MeshPrimitive *mesh, float density, const Imath::V2f &offset, const std::string &densityMask, const std::string &uvSet, const std::string &refPosition, const Canceller *canceller )
316+ PointsPrimitivePtr MeshAlgo::distributePoints ( const MeshPrimitive *mesh, float density, const Imath::V2f &offset, const std::string &densityMask, const std::string &uvSet, const std::string &refPosition, const StringAlgo::MatchPattern &primitiveVariables, const Canceller *canceller )
260317{
261318 if ( density < 0 )
262319 {
263320 throw InvalidArgumentException ( " MeshAlgo::distributePoints : The density of the distribution cannot be negative." );
264321 }
265322
266- MeshPrimitivePtr triangulatedMesh ;
323+ MeshPrimitivePtr processedMesh ;
267324 PrimitiveVariable uvVar, faceAreaVar, textureAreaVar, densityVar;
268325
269326 // Make sure we have a triangulated mesh, and valid values for all the primitive variables we need.
270327 processInputs (
271- mesh, refPosition, uvSet, densityMask, canceller,
272- triangulatedMesh , uvVar, faceAreaVar, textureAreaVar, densityVar
328+ mesh, refPosition, uvSet, densityMask, primitiveVariables, canceller,
329+ processedMesh , uvVar, densityVar, faceAreaVar, textureAreaVar
273330 );
274331
275332 PrimitiveVariable::IndexedView<V2f> uvView ( uvVar );
@@ -287,8 +344,8 @@ PointsPrimitivePtr MeshAlgo::distributePoints( const MeshPrimitive *mesh, float
287344 densityView = PrimitiveVariable::IndexedView<float >( densityVar );
288345 }
289346
290- const size_t numFaces = triangulatedMesh ->verticesPerFace ()->readable ().size ();
291- const std::vector<int > &vertexIds = triangulatedMesh ->vertexIds ()->readable ();
347+ const size_t numFaces = processedMesh ->verticesPerFace ()->readable ().size ();
348+ const std::vector<int > &vertexIds = processedMesh ->vertexIds ()->readable ();
292349
293350 const int facesPerChunk = std::max ( 1 , std::min ( (int )( numFaces / 100 ), 10000 ) );
294351 const int numChunks = ( numFaces + facesPerChunk - 1 ) / facesPerChunk;
@@ -340,33 +397,131 @@ PointsPrimitivePtr MeshAlgo::distributePoints( const MeshPrimitive *mesh, float
340397 numPoints += chunkResults[chunkIndex].size ();
341398 }
342399
343- const PrimitiveVariable::IndexedView<Imath::V3f> pView = triangulatedMesh->variableIndexedView <V3fVectorData>(
344- " P" , PrimitiveVariable::Interpolation::Vertex, /* throwIfInvalid */ true
345- ).value ();
400+ PointsPrimitivePtr result = new PointsPrimitive ( numPoints );
401+
402+ struct ToResample
403+ {
404+ PrimitiveVariable::Interpolation sourceInterpolation;
405+ std::any sourceView;
406+ IECore::Data *target;
407+ };
408+ std::vector< ToResample > toResample;
409+
410+ for ( auto &i : processedMesh->variables )
411+ {
412+ if ( i.second .interpolation == PrimitiveVariable::Constant )
413+ {
414+ result->variables [i.first ] = i.second ;
415+ }
416+ else
417+ {
418+ PrimitiveVariable::Interpolation sourceInterpolation = i.second .interpolation ;
419+ dispatch ( i.second .data .get (),
420+ [ &i, numPoints, sourceInterpolation, &result, &toResample]( const auto *sourceData )
421+ {
422+ using DataType = typename std::remove_const_t < std::remove_pointer_t < decltype ( sourceData ) > >;
423+ if constexpr ( !TypeTraits::IsVectorTypedData<DataType>::value )
424+ {
425+ throw IECore::Exception ( " MeshAlgo::distributePoints : Invalid PrimitiveVariable, data is not a vector." );
426+ }
427+ else
428+ {
429+ using ElementType = typename DataType::ValueType::value_type;
430+ if (
431+ sourceInterpolation != PrimitiveVariable::Uniform &&
432+ !supportsAddMult<ElementType>()
433+ )
434+ {
435+ throw IECore::Exception ( " MeshAlgo::distributePoints : Cannot interpolate " + i.first );
436+ }
437+
438+ typename DataType::Ptr newData = new DataType;
439+ newData->writable ().resize ( numPoints );
440+
441+ if constexpr ( TypeTraits::IsGeometricTypedData< DataType >::value )
442+ {
443+ if ( i.first == " P" )
444+ {
445+ newData->setInterpretation ( GeometricData::Point );
446+ }
447+ else
448+ {
449+ newData->setInterpretation ( sourceData->getInterpretation () );
450+ }
451+ }
452+
453+ result->variables [i.first ] = PrimitiveVariable ( PrimitiveVariable::Vertex, newData );
454+ toResample.push_back ( {
455+ i.second .interpolation ,
456+ PrimitiveVariable::IndexedView< ElementType >( i.second ),
457+ newData.get ()
458+ } );
459+ }
460+ }
461+ );
462+ }
463+ }
346464
347465 Canceller::check ( canceller );
348- V3fVectorDataPtr pNewData = new V3fVectorData ();
349- std::vector<V3f> &pNew = pNewData->writable ();
350- pNew.resize ( numPoints );
351466
352467 // Use the barycentric coordinates to sample all the primitive variables we need
353468 tbb::parallel_for (
354469 tbb::blocked_range<size_t >( 0 , numChunks ),
355- [&chunkResults, &chunkOffsets, &vertexIds, &pView, &pNew , canceller]( const tbb::blocked_range<size_t > &range )
470+ [&chunkResults, &chunkOffsets, &vertexIds, &toResample , canceller]( const tbb::blocked_range<size_t > &range )
356471 {
357- for ( size_t chunkIndex = range. begin (); chunkIndex != range. end (); ++chunkIndex )
472+ for ( auto &var : toResample )
358473 {
359- int outputIndex = chunkOffsets[chunkIndex];
360- Canceller::check ( canceller );
361- for ( auto &i : chunkResults[chunkIndex] )
362- {
363- pNew[outputIndex] = triangleInterpolatedPrimVarValue ( PrimitiveVariable::Vertex, pView, vertexIds, i.faceIdx , i.bary );
364- outputIndex++;
365- }
474+ dispatch ( var.target ,
475+ [ &var, &vertexIds, &chunkResults, &chunkOffsets, &range, canceller ]( auto *targetData )
476+ {
477+ using DataType = typename std::remove_const_t < std::remove_pointer_t < decltype ( targetData ) > >;
478+ if constexpr ( TypeTraits::IsVectorTypedData<DataType>::value )
479+ {
480+ using ElementType = typename DataType::ValueType::value_type;
481+ const PrimitiveVariable::IndexedView<ElementType> &view = std::any_cast<PrimitiveVariable::IndexedView< ElementType > >( var.sourceView );
482+ auto &target = targetData->writable ();
483+
484+ Canceller::check ( canceller );
485+ for ( size_t chunkIndex = range.begin (); chunkIndex != range.end (); ++chunkIndex )
486+ {
487+ int outputIndex = chunkOffsets[chunkIndex];
488+
489+ if ( var.sourceInterpolation == PrimitiveVariable::Uniform )
490+ {
491+ for ( auto &i : chunkResults[chunkIndex] )
492+ {
493+ target[outputIndex] = view[ i.faceIdx ];
494+ outputIndex++;
495+ }
496+ }
497+ else if constexpr ( supportsAddMult<ElementType>() )
498+ {
499+ for ( auto &i : chunkResults[chunkIndex] )
500+ {
501+ target[outputIndex] = triangleInterpolatedPrimVarValue (
502+ var.sourceInterpolation , view, vertexIds, i.faceIdx , i.bary
503+ );
504+ outputIndex++;
505+ }
506+ }
507+ else
508+ {
509+ // This branch never taken, because earlier test throws for non-uniform
510+ // non-interpolable
511+ }
512+ }
513+ }
514+ }
515+ );
366516 }
367517 },
368518 taskGroupContext
369519 );
520+ return result;
521+ }
370522
371- return new PointsPrimitive ( pNewData );
523+ // Old signature for backwards compatibility
524+ PointsPrimitivePtr MeshAlgo::distributePoints ( const MeshPrimitive *mesh, float density, const Imath::V2f &offset, const std::string &densityMask, const std::string &uvSet, const std::string &refPosition, const Canceller *canceller )
525+ {
526+ return MeshAlgo::distributePoints ( mesh, density, offset, densityMask, uvSet, refPosition, " " , canceller );
372527}
0 commit comments