Skip to content

Commit 2922055

Browse files
MeshAlgo::distributePoints : Added primitiveVariables parameter
1 parent 647b35f commit 2922055

File tree

4 files changed

+256
-39
lines changed

4 files changed

+256
-39
lines changed

include/IECoreScene/MeshAlgo.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
#include "IECore/Canceller.h"
3939
#include "IECore/ImathHash.h"
40+
#include "IECore/StringAlgo.h"
4041
#include "IECoreScene/MeshPrimitive.h"
4142
#include "IECoreScene/PointsPrimitive.h"
4243
#include "IECoreScene/PrimitiveVariable.h"
@@ -94,6 +95,9 @@ IECORESCENE_API void reorderVertices( MeshPrimitive *mesh, int id0, int id1, int
9495
/// Distributes points over a mesh using an IECore::PointDistribution in UV space
9596
/// and mapping it to 3d space. It gives a fairly even distribution regardless of
9697
/// vertex spacing, provided the UVs are well layed out.
98+
IECORESCENE_API PointsPrimitivePtr distributePoints( const MeshPrimitive *mesh, float density = 100.0, const Imath::V2f &offset = Imath::V2f( 0 ), const std::string &densityMask = "density", const std::string &uvSet = "uv", const std::string &refPosition = "P", const IECore::StringAlgo::MatchPattern &primitiveVariables = "", const IECore::Canceller *canceller = nullptr );
99+
100+
// Deprecated signature without support for primitiveVariables
97101
IECORESCENE_API PointsPrimitivePtr distributePoints( const MeshPrimitive *mesh, float density = 100.0, const Imath::V2f &offset = Imath::V2f( 0 ), const std::string &densityMask = "density", const std::string &uvSet = "uv", const std::string &refPosition = "P", const IECore::Canceller *canceller = nullptr );
98102

99103

src/IECoreScene/MeshAlgoDistributePoints.cpp

Lines changed: 191 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,17 @@
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+
4549
using namespace Imath;
4650
using namespace IECore;
4751
using 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(
134139
void 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
}

src/IECoreScene/bindings/MeshAlgoBinding.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,10 @@ void reorderVerticesWrapper( MeshPrimitive *mesh, int id0, int id1, int id2, con
155155
return MeshAlgo::reorderVertices( mesh, id0, id1, id2, canceller );
156156
}
157157

158-
PointsPrimitivePtr distributePointsWrapper( const MeshPrimitive *mesh, float density, const Imath::V2f &offset, const std::string &densityMask, const std::string &uvSet, const std::string &refPosition, const IECore::Canceller *canceller )
158+
PointsPrimitivePtr distributePointsWrapper( const MeshPrimitive *mesh, float density, const Imath::V2f &offset, const std::string &densityMask, const std::string &uvSet, const std::string &refPosition, const IECore::StringAlgo::MatchPattern &primitiveVariables, const IECore::Canceller *canceller )
159159
{
160160
ScopedGILRelease gilRelease;
161-
return MeshAlgo::distributePoints( mesh, density, offset, densityMask, uvSet, refPosition, canceller );
161+
return MeshAlgo::distributePoints( mesh, density, offset, densityMask, uvSet, refPosition, primitiveVariables, canceller );
162162
}
163163

164164
boost::python::list segmentWrapper(const MeshPrimitive *mesh, const PrimitiveVariable &primitiveVariable, const IECore::Data *segmentValues = nullptr, const IECore::Canceller *canceller = nullptr )
@@ -247,7 +247,7 @@ void bindMeshAlgo()
247247
def( "deleteFaces", &deleteFacesWrapper, ( arg_( "meshPrimitive" ), arg_( "facesToDelete" ), arg_( "invert" ) = false, arg_( "canceller" ) = object() ) );
248248
def( "reverseWinding", &reverseWindingWrapper, ( arg_( "meshPrimitive" ), arg_( "canceller" ) = object() ) );
249249
def( "reorderVertices", &reorderVerticesWrapper, ( arg_( "mesh" ), arg_( "id0" ), arg_( "id1" ), arg_( "id2" ), arg_( "canceller" ) = object() ) );
250-
def( "distributePoints", &distributePointsWrapper, ( arg_( "mesh" ), arg_( "density" ) = 100.0, arg_( "offset" ) = Imath::V2f( 0 ), arg_( "densityMask" ) = "density", arg_( "uvSet" ) = "uv", arg_( "refPosition" ) = "P", arg_( "canceller" ) = object() ) );
250+
def( "distributePoints", &distributePointsWrapper, ( arg_( "mesh" ), arg_( "density" ) = 100.0, arg_( "offset" ) = Imath::V2f( 0 ), arg_( "densityMask" ) = "density", arg_( "uvSet" ) = "uv", arg_( "refPosition" ) = "P", arg( "primitiveVariables" ) = "", arg_( "canceller" ) = object() ) );
251251
def( "segment", &::segmentWrapper, segmentOverLoads() );
252252
def( "merge", &::mergeWrapper, ( arg_( "meshes" ), arg_( "canceller" ) = object() ) );
253253
def( "triangulate", &triangulateWrapper, (arg_("mesh"), arg_( "canceller" ) = object() ) );

0 commit comments

Comments
 (0)