Skip to content

Commit cdf6a7a

Browse files
authored
Merge pull request #1441 from danieldresser-ie/splitOrdered
MeshAlgo::MeshSplitter : Preserve vertex order
2 parents 6fd1532 + a5885ef commit cdf6a7a

File tree

4 files changed

+91
-27
lines changed

4 files changed

+91
-27
lines changed

Changes

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ Improvements
88
- IECoreHoudini : Updated to support Houdini 20.0 and 20.5.
99
- IECoreMaya : Avoid compilation warnings with new gcc.
1010

11+
Fixes
12+
-----
13+
14+
- MeshAlgo::MeshSplitter/segment : Fixed so that we now preserve vertex order while splitting. This matches the old behvaviour before 1.4.6.0 when segment was optimized. This doesn't affect the correctness of the result, but is a better convention to match user expectations - when combining meshes followed by splitting, it's better if you get back the same vertex ids you started with.
15+
1116
Build
1217
-----
1318

src/IECoreScene/MeshAlgoSplit.cpp

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,8 @@ class Reindexer
490490
m_newIndices( m_newIndicesData->writable() ),
491491
m_blockSize( blockSize ),
492492
m_fromOldIds( ( numOriginalIds - 1 ) / blockSize + 1 ),
493-
m_numIdsUsed( 0 )
493+
m_numIdsUsed( 0 ),
494+
m_indicesComputed( false )
494495
{
495496
m_newIndices.reserve( numIndices );
496497
}
@@ -510,21 +511,21 @@ class Reindexer
510511
block = std::make_unique< std::vector<int> >( m_blockSize, -1 );
511512
}
512513

513-
int &r = (*block)[ subIndex ];
514-
if( r == -1 )
515-
{
516-
// Id isn't used yet, we need to set this location in the block, and use it
517-
r = m_numIdsUsed;
518-
m_numIdsUsed++;
519-
}
514+
// We initially record that this index is used just by marking it with a 0, against the background of -1.
515+
// Once computeIndices is called, the 0 will be replaced with a new index, only counting indices that are
516+
// used.
517+
(*block)[ subIndex ] = 0;
520518

521-
m_newIndices.push_back( r );
519+
m_newIndices.push_back( id );
520+
521+
m_indicesComputed = false;
522522
}
523523

524524
// Don't add the index, but just test if it is a part of the reindex. If it is an
525525
// id which has already been added, return the new id, otherwise return -1
526526
inline int testIndex( int id )
527527
{
528+
computeIndices();
528529
int blockId = id / m_blockSize;
529530
int subIndex = id % m_blockSize;
530531
auto &block = m_fromOldIds[ blockId ];
@@ -541,6 +542,7 @@ class Reindexer
541542
// Get the new indices. Call after calling addIndex for every original index
542543
IntVectorDataPtr getNewIndices()
543544
{
545+
computeIndices();
544546
return m_newIndicesData;
545547
}
546548

@@ -550,6 +552,7 @@ class Reindexer
550552
template <typename T >
551553
void remapData( const std::vector<T> &in, std::vector<T> &out )
552554
{
555+
computeIndices();
553556
out.resize( m_numIdsUsed );
554557
for( unsigned int i = 0; i < m_fromOldIds.size(); i++ )
555558
{
@@ -574,6 +577,7 @@ class Reindexer
574577
// original id corresponding to each id of the output
575578
void getDataRemapping( std::vector<int> &dataRemap )
576579
{
580+
computeIndices();
577581
dataRemap.resize( m_numIdsUsed );
578582
for( unsigned int i = 0; i < m_fromOldIds.size(); i++ )
579583
{
@@ -595,6 +599,45 @@ class Reindexer
595599
}
596600

597601
private:
602+
603+
void computeIndices()
604+
{
605+
// Once indices have been added, and before using them, this function is called to
606+
// compute the new indices.
607+
if( m_indicesComputed )
608+
{
609+
return;
610+
}
611+
612+
m_indicesComputed = true;
613+
614+
for( unsigned int blockId = 0; blockId < m_fromOldIds.size(); blockId++ )
615+
{
616+
auto &block = m_fromOldIds[ blockId ];
617+
if( !block )
618+
{
619+
continue;
620+
}
621+
622+
for( int i = 0; i < m_blockSize; i++ )
623+
{
624+
if( (*block)[i] != -1 )
625+
{
626+
(*block)[i] = m_numIdsUsed;
627+
m_numIdsUsed++;
628+
}
629+
}
630+
}
631+
632+
for( int &id : m_newIndices )
633+
{
634+
int blockId = id / m_blockSize;
635+
int subIndex = id % m_blockSize;
636+
637+
id = (*m_fromOldIds[ blockId ])[subIndex];
638+
}
639+
}
640+
598641
// IntVectorData to hold the new indices
599642
IntVectorDataPtr m_newIndicesData;
600643
std::vector< int > &m_newIndices;
@@ -605,13 +648,16 @@ class Reindexer
605648
// Store the mapping from old ids to new ids. The outer vector holds a unique_ptr for each
606649
// block of m_blockSize ids in the original id range. These pointers are null if no ids from
607650
// that block have been used. Once a block is used, it is allocated with a vector that is set
608-
// to -1 for ids which have not been used, and the new corresponding id for ids which have been
609-
// used
651+
// to -1 for ids which have not been used, and zeros for ids which have been used. When computeIndices()
652+
// is called, all used elements get a new id assigned, relative to just the used ids.
610653
std::vector< std::unique_ptr< std::vector< int > > > m_fromOldIds;
611654

612655
// How many unique ids have appeared in the indices added so far
613656
int m_numIdsUsed;
614657

658+
// Whether we have yet computed the new indices for each used index
659+
bool m_indicesComputed;
660+
615661
};
616662

617663
struct ResamplePrimitiveVariableFunctor

test/IECoreScene/MeshAlgoSegmentTest.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ def testCanSegmentUsingIntegerPrimvar( self ) :
6666
p12 = imath.V3f( 1, 2, 0 )
6767
p22 = imath.V3f( 2, 2, 0 )
6868

69-
self.assertEqual( segments[0]["P"].data, IECore.V3fVectorData( [p00, p10, p11, p01, p20, p21], IECore.GeometricData.Interpretation.Point ) )
70-
self.assertEqual( segments[1]["P"].data, IECore.V3fVectorData( [p01, p11, p12, p02, p21, p22], IECore.GeometricData.Interpretation.Point ) )
69+
self.assertEqual( segments[0]["P"].data, IECore.V3fVectorData( [p00, p10, p20, p01, p11, p21], IECore.GeometricData.Interpretation.Point ) )
70+
self.assertEqual( segments[1]["P"].data, IECore.V3fVectorData( [p01, p11, p21, p02, p12, p22], IECore.GeometricData.Interpretation.Point ) )
7171

7272
def testCanSegmentUsingStringPrimvar( self ) :
7373
mesh = IECoreScene.MeshPrimitive.createPlane( imath.Box2f( imath.V2f( 0 ), imath.V2f( 2 ) ), imath.V2i( 2 ) )
@@ -95,8 +95,8 @@ def testCanSegmentUsingStringPrimvar( self ) :
9595
p12 = imath.V3f( 1, 2, 0 )
9696
p22 = imath.V3f( 2, 2, 0 )
9797

98-
self.assertEqual( segments[0]["P"].data, IECore.V3fVectorData( [p00, p10, p11, p01, p21, p22, p12], IECore.GeometricData.Interpretation.Point ) )
99-
self.assertEqual( segments[1]["P"].data, IECore.V3fVectorData( [p10, p20, p21, p11, p01, p12, p02], IECore.GeometricData.Interpretation.Point ) )
98+
self.assertEqual( segments[0]["P"].data, IECore.V3fVectorData( [p00, p10, p01, p11, p21, p12, p22], IECore.GeometricData.Interpretation.Point ) )
99+
self.assertEqual( segments[1]["P"].data, IECore.V3fVectorData( [p10, p20, p01, p11, p21, p02, p12], IECore.GeometricData.Interpretation.Point ) )
100100

101101
def testSegmentsFullyIfNoSegmentValuesGiven( self ) :
102102
mesh = IECoreScene.MeshPrimitive.createPlane( imath.Box2f( imath.V2f( 0 ), imath.V2f( 2 ) ), imath.V2i( 2 ) )
@@ -130,8 +130,8 @@ def testSegmentsFullyIfNoSegmentValuesGiven( self ) :
130130
s0 = segments[1]
131131
s1 = segments[0]
132132

133-
self.assertEqual( s0["P"].data, IECore.V3fVectorData( [p00, p10, p11, p01, p21, p22, p12], IECore.GeometricData.Interpretation.Point ) )
134-
self.assertEqual( s1["P"].data, IECore.V3fVectorData( [p10, p20, p21, p11, p01, p12, p02], IECore.GeometricData.Interpretation.Point ) )
133+
self.assertEqual( s0["P"].data, IECore.V3fVectorData( [p00, p10, p01, p11, p21, p12, p22], IECore.GeometricData.Interpretation.Point ) )
134+
self.assertEqual( s1["P"].data, IECore.V3fVectorData( [p10, p20, p01, p11, p21, p02, p12], IECore.GeometricData.Interpretation.Point ) )
135135

136136

137137
def testRaisesExceptionIfSegmentKeysNotSameTypeAsPrimvar( self ) :
@@ -184,7 +184,7 @@ def testSegmentSubset( self ) :
184184
p22 = imath.V3f( 2, 2, 0 )
185185

186186
self.assertEqual( len( segments ), 1 )
187-
self.assertEqual( segments[0]["P"].data, IECore.V3fVectorData( [p11, p21, p22, p12], IECore.GeometricData.Interpretation.Point ) )
187+
self.assertEqual( segments[0]["P"].data, IECore.V3fVectorData( [p11, p21, p12, p22], IECore.GeometricData.Interpretation.Point ) )
188188
self.assertEqual( segments[0]["s"].data, IECore.StringVectorData( ["b"] ) )
189189

190190

test/IECoreScene/MeshAlgoSplitTest.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -170,16 +170,29 @@ def accumulateInPython2( iterable ):
170170
newVerticesPerFace = []
171171
newVertIndices = []
172172
mapFaceVert = []
173-
reverseIndices = []
173+
174+
usedIndices = set()
174175
for i in r:
175176
vpf = mesh.verticesPerFace[i]
176177
newVerticesPerFace.append( vpf )
177178
for j in range( vpf ):
178179
origFaceVert = faceIndices[i] + j
179180
origVert = mesh.vertexIds[ origFaceVert ]
180-
newIndex = reindex.setdefault( origVert, len( reindex ) )
181-
if len( reindex ) > len( reverseIndices ):
182-
reverseIndices.append( origVert )
181+
182+
usedIndices.add( origVert )
183+
184+
usedIndices = sorted( usedIndices )
185+
186+
for i in range( len( usedIndices ) ):
187+
reindex[ usedIndices[i] ] = i
188+
189+
for i in r:
190+
vpf = mesh.verticesPerFace[i]
191+
for j in range( vpf ):
192+
origFaceVert = faceIndices[i] + j
193+
origVert = mesh.vertexIds[ origFaceVert ]
194+
195+
newIndex = usedIndices.index( origVert )
183196
newVertIndices.append( newIndex )
184197
mapFaceVert.append( origFaceVert )
185198

@@ -226,7 +239,7 @@ def accumulateInPython2( iterable ):
226239
if p.interpolation == interp.Constant:
227240
d = p.data.copy()
228241
elif p.interpolation in [ interp.Vertex, interp.Varying ]:
229-
d = type( p.data )( [ pd[i] for i in reverseIndices] )
242+
d = type( p.data )( [ pd[i] for i in usedIndices] )
230243
elif p.interpolation == interp.FaceVarying:
231244
d = type( p.data )( [ pd[i] for i in mapFaceVert ] )
232245
elif p.interpolation == interp.Uniform:
@@ -269,8 +282,8 @@ def testCanSplitUsingIntegerPrimvar( self ) :
269282
p12 = imath.V3f( 1, 2, 0 )
270283
p22 = imath.V3f( 2, 2, 0 )
271284

272-
self.assertEqual( s0["P"].data, IECore.V3fVectorData( [p00, p10, p11, p01, p20, p21], IECore.GeometricData.Interpretation.Point ) )
273-
self.assertEqual( s1["P"].data, IECore.V3fVectorData( [p01, p11, p12, p02, p21, p22], IECore.GeometricData.Interpretation.Point ) )
285+
self.assertEqual( s0["P"].data, IECore.V3fVectorData( [p00, p10, p20, p01, p11, p21], IECore.GeometricData.Interpretation.Point ) )
286+
self.assertEqual( s1["P"].data, IECore.V3fVectorData( [p01, p11, p21, p02, p12, p22], IECore.GeometricData.Interpretation.Point ) )
274287

275288
def testSplitsFully( self ) :
276289
mesh = IECoreScene.MeshPrimitive.createPlane( imath.Box2f( imath.V2f( 0 ), imath.V2f( 2 ) ), imath.V2i( 2 ) )
@@ -303,8 +316,8 @@ def testSplitsFully( self ) :
303316
if s0["s"].data[0] != 'a':
304317
s0,s1 = s1,s0
305318

306-
self.assertEqual( s0["P"].data, IECore.V3fVectorData( [p00, p10, p11, p01, p21, p22, p12], IECore.GeometricData.Interpretation.Point ) )
307-
self.assertEqual( s1["P"].data, IECore.V3fVectorData( [p10, p20, p21, p11, p01, p12, p02], IECore.GeometricData.Interpretation.Point ) )
319+
self.assertEqual( s0["P"].data, IECore.V3fVectorData( [p00, p10, p01, p11, p21, p12, p22], IECore.GeometricData.Interpretation.Point ) )
320+
self.assertEqual( s1["P"].data, IECore.V3fVectorData( [p10, p20, p01, p11, p21, p02, p12], IECore.GeometricData.Interpretation.Point ) )
308321

309322
def testSplitUsingIndexedPrimitiveVariable( self ) :
310323

0 commit comments

Comments
 (0)