@@ -57,6 +57,7 @@ SOFTWARE.
57
57
#include < vector>
58
58
#include < algorithm>
59
59
#include < numeric>
60
+ #include < math.h>
60
61
61
62
#define CHECKSTAT (m ) if (!status) {status.perror (m); return status;};
62
63
@@ -72,13 +73,17 @@ SOFTWARE.
72
73
#define GB_PIN 1
73
74
#define GB_SLIDE 2
74
75
76
+ #define SA_LAPLACIAN 0
77
+ #define SA_TAUBIN 1
78
+
75
79
#define DEFORMER_NAME " BlurRelax"
76
80
77
81
// Double vs Float
78
82
#define float_t double
79
83
#define point_t MPoint
80
84
#define pointArray_t MPointArray
81
85
86
+
82
87
void edgeProject (
83
88
const float_t basePoints[][4 ],
84
89
const std::vector<size_t > &group,
@@ -174,8 +179,8 @@ void quickLaplacianSmooth(
174
179
const std::vector<std::vector<size_t >> &neighbors,
175
180
const std::vector<float_t > &valence,
176
181
const std::vector<float_t > &shiftVal,
177
- const std::vector<float_t > &shiftComp ,
178
- const std::vector< bool > &pinPoints
182
+ const std::vector<bool > &pinPoints ,
183
+ const float_t taubinBias= 1.0
179
184
) {
180
185
/*
181
186
All the crazy hoops I've jumped through are to make auto-vectorization work
@@ -184,23 +189,19 @@ void quickLaplacianSmooth(
184
189
of a std::vector of neighbors per vert with that vector sorted so the verts
185
190
with the most neighbors were at the top.
186
191
neighborOffsets contains the index offsets of vertices with at least [index] of neighbors
187
- So a single-subdivided cube would have 8 3-valence, and 18 4-valence for a total of 26 verts, and 96 neighbors
188
- So the neighborOffsets would be [0, 26, 52, 78, 96] meaning that
189
- verts 0-25 have at least 1 neighbor
190
- verts 26-51 have at least 2 neighbors
191
- verts 52-77 have at least 3 neighbors
192
- verts 78-95 have at least 4 neighbors
193
192
*/
194
193
195
- // First, get verts as a single pointer to the contiguous memory stored in (verts *)[4]
194
+ // First, get verts as a single pointer to the contiguous memory stored in (verts2d *)[4]
196
195
float_t * verts = &(verts2d[0 ][0 ]);
197
196
198
197
// number of nonzero valence
199
198
size_t nzv = neighbors[0 ].size ();
199
+
200
+ // number of nonzero components
200
201
size_t nzc = 4 * nzv;
201
202
202
203
// The __restrict keyword tells the compiler that *outComp
203
- // are not pointed to by any other pointer in this scope
204
+ // is not pointed to by any other pointer in this scope
204
205
// This allows for auto-vectorization
205
206
float_t * __restrict outComp = new float_t [nzc];
206
207
memset (outComp, 0 , nzc*sizeof (float_t ));
@@ -216,10 +217,8 @@ void quickLaplacianSmooth(
216
217
}
217
218
}
218
219
219
- // Depending on the compiler optimization, it may be faster to break up this line
220
- // Gotta test
221
220
for (size_t i = 0 ; i < nzc; ++i) {
222
- outComp[i] = shiftVal[i] * ( outComp[i] / valence[i]) + shiftComp [i] * verts[i];
221
+ outComp[i] = shiftVal[i] * taubinBias * (( outComp[i] / valence[i]) - verts [i]) + verts[i];
223
222
}
224
223
225
224
memcpy (verts, outComp, nzc*sizeof (float_t ));
@@ -244,7 +243,7 @@ class BlurRelax : public MPxDeformerNode {
244
243
static MObject aHardEdgeBehavior;
245
244
static MObject aGroupEdgeBehavior;
246
245
static MObject aReproject;
247
-
246
+ static MObject aTaubinBias;
248
247
static MTypeId id;
249
248
private:
250
249
@@ -269,7 +268,6 @@ class BlurRelax : public MPxDeformerNode {
269
268
std::vector<std::vector<size_t >> &neighbors,
270
269
std::vector<std::vector<bool >> &hardEdges,
271
270
std::vector<float_t > &shiftVal, // normally 0.5, but it's 0.25 if on a hard edge
272
- std::vector<float_t > &shiftComp, // normally 0.5, but it's 0.75 if on a hard edge
273
271
std::vector<float_t > &valence, // as float for vectorizing
274
272
std::vector<bool > &pinPoints,
275
273
std::vector<UINT> &creaseCount,
@@ -282,15 +280,15 @@ class BlurRelax : public MPxDeformerNode {
282
280
const short hardEdgeBehavior,
283
281
const short groupEdgeBehavior,
284
282
const bool reproject,
285
- const UINT iterations,
283
+ const float taubinBias,
284
+ const float_t iterations,
286
285
const UINT numVerts,
287
286
const std::vector<bool > &group,
288
287
const std::vector<size_t > &order,
289
288
const std::vector<size_t > &invOrder,
290
289
const std::vector<std::vector<size_t >> &neighbors,
291
290
const std::vector<std::vector<bool >> &hardEdges,
292
291
const std::vector<float_t > &shiftVal, // normally 0.5, but it's 0.25 if on a hard edge
293
- const std::vector<float_t > &shiftComp, // normally 0.5, but it's 0.75 if on a hard edge
294
292
const std::vector<float_t > &valence, // as float for vectorizing
295
293
const std::vector<bool > &pinPoints,
296
294
const std::vector<UINT> &creaseCount,
@@ -306,6 +304,8 @@ MObject BlurRelax::aBorderBehavior;
306
304
MObject BlurRelax::aHardEdgeBehavior;
307
305
MObject BlurRelax::aGroupEdgeBehavior;
308
306
MObject BlurRelax::aReproject;
307
+ MObject BlurRelax::aTaubinBias;
308
+
309
309
310
310
BlurRelax::BlurRelax () {}
311
311
BlurRelax::~BlurRelax () {}
@@ -423,9 +423,20 @@ MStatus BlurRelax::initialize() {
423
423
status = attributeAffects (aReproject, outputGeom);
424
424
CHECKSTAT (" aReproject" );
425
425
426
- aIterations = nAttr.create (" iterations" , " i" , MFnNumericData::kInt , 10 , &status);
426
+ // Taubin Bias is divided by 1000 internally
427
+ aTaubinBias = nAttr.create (" preserveVolume" , " pv" , MFnNumericData::kFloat , 0 .0f , &status);
428
+ CHECKSTAT (" aTaubinBias" );
429
+ nAttr.setMin (0 .0f );
430
+ nAttr.setMax (2 .0f );
431
+ nAttr.setChannelBox (true );
432
+ status = addAttribute (aTaubinBias);
433
+ CHECKSTAT (" aTaubinBias" );
434
+ status = attributeAffects (aTaubinBias, outputGeom);
435
+ CHECKSTAT (" aTaubinBias" );
436
+
437
+ aIterations = nAttr.create (" iterations" , " i" , MFnNumericData::kFloat , 0 , &status);
427
438
CHECKSTAT (" aIterations" );
428
- nAttr.setMin (0 );
439
+ nAttr.setMin (0.0 );
429
440
nAttr.setChannelBox (true );
430
441
status = addAttribute (aIterations);
431
442
CHECKSTAT (" aIterations" );
@@ -444,7 +455,7 @@ MStatus BlurRelax::deform(MDataBlock& dataBlock, MItGeometry& vertIter, const MM
444
455
float env = hEnv.asFloat ();
445
456
446
457
MDataHandle hIter = dataBlock.inputValue (aIterations);
447
- int iterations = hIter.asInt ();
458
+ float iterations = hIter.asFloat ();
448
459
449
460
if (iterations > 0 && env > 0 .0f ){
450
461
// Get the data from the node
@@ -457,6 +468,23 @@ MStatus BlurRelax::deform(MDataBlock& dataBlock, MItGeometry& vertIter, const MM
457
468
MDataHandle hReproject = dataBlock.inputValue (aReproject);
458
469
bool reproject = hReproject.asBool ();
459
470
471
+ MDataHandle hTBias = dataBlock.inputValue (aTaubinBias);
472
+ float tBias = hTBias.asFloat ();
473
+ // volume preservation uses 2 steps per iteration
474
+ // so half the number of iterations if I'm volume preserving
475
+ // The iterations interpolating as floats takes care of 99% of the jumping
476
+ // There *will* be a tiny jump from 0.0 on preserve volume if
477
+ // reprojection is also turned on. I don't think I can get rid of that one
478
+ if (tBias > 0 .0f ) {
479
+ iterations /= 2 .0f ;
480
+ }
481
+ // So the numbers shown are intuitive to the user
482
+ // 0 maps to 1.0 and 1 maps to -1.05
483
+ // -1.05 is used because taubin smoothing needs something
484
+ // just a little over 1.0 to truly preserve volume,
485
+ // and that value looked good on my test mesh.
486
+ tBias = -2 .05f * tBias + 1 .0f ;
487
+
460
488
// get the input mesh corresponding to this output
461
489
MObject thisNode = this ->thisMObject ();
462
490
MPlug inPlug (thisNode, input);
@@ -471,7 +499,6 @@ MStatus BlurRelax::deform(MDataBlock& dataBlock, MItGeometry& vertIter, const MM
471
499
std::vector<std::vector<size_t >> neighbors;
472
500
std::vector<std::vector<bool >> hardEdges;
473
501
std::vector<float_t > shiftVal; // normally 0.5; but it's 0.25 if on a hard edge
474
- std::vector<float_t > shiftComp; // normally 0.5; but it's 0.75 if on a hard edge
475
502
std::vector<float_t > valence; // as float for vectorizing
476
503
std::vector<bool > pinPoints;
477
504
std::vector<UINT> creaseCount;
@@ -489,7 +516,7 @@ MStatus BlurRelax::deform(MDataBlock& dataBlock, MItGeometry& vertIter, const MM
489
516
// Populate the variables with *SPECIALLY ORDERED* data
490
517
// all vertex data is now shuffled by the order vector
491
518
buildQuickData (mesh, vertIter, bb, hb, gb, reproject,
492
- group, order, invOrder, neighbors, hardEdges, shiftVal, shiftComp, valence,
519
+ group, order, invOrder, neighbors, hardEdges, shiftVal, valence,
493
520
pinPoints, creaseCount, verts);
494
521
495
522
// This can happen if the user is pinning all the points
@@ -500,9 +527,9 @@ MStatus BlurRelax::deform(MDataBlock& dataBlock, MItGeometry& vertIter, const MM
500
527
}
501
528
502
529
// Calculate the relax, and store in verts
503
- quickRelax (mesh, bb, hb, gb, reproject,
530
+ quickRelax (mesh, bb, hb, gb, reproject, tBias,
504
531
iterations, numVerts, group, order, invOrder, neighbors, hardEdges, shiftVal,
505
- shiftComp, valence, pinPoints, creaseCount, verts);
532
+ valence, pinPoints, creaseCount, verts);
506
533
507
534
// Get the painted weight values
508
535
std::vector<float > weightVals;
@@ -548,7 +575,6 @@ void BlurRelax::buildQuickData(
548
575
std::vector<std::vector<size_t >> &neighbors,
549
576
std::vector<std::vector<bool >> &hardEdges,
550
577
std::vector<float_t > &shiftVal, // normally 0.5, but it's 0.25 if on a hard edge
551
- std::vector<float_t > &shiftComp, // normally 0.5, but it's 0.75 if on a hard edge
552
578
std::vector<float_t > &valence, // as float for vectorizing
553
579
std::vector<bool > &pinPoints,
554
580
std::vector<UINT> &creaseCount,
@@ -665,11 +691,8 @@ void BlurRelax::buildQuickData(
665
691
// if a vert has a pinned neighbor, remove it
666
692
667
693
std::vector<float_t > rawShiftVal;
668
- std::vector<float_t > rawShiftComp;
669
694
rawShiftVal.resize (numVertices);
670
- rawShiftComp.resize (numVertices);
671
695
std::fill (rawShiftVal.begin (), rawShiftVal.end (), 0.5 );
672
- std::fill (rawShiftComp.begin (), rawShiftComp.end (), 0.5 );
673
696
674
697
for (size_t i = 0 ; i < rawNeighbors.size (); ++i) {
675
698
if ((rawCreaseCount[i] != 0 ) || rawPinPoints[i]) {
@@ -689,7 +712,6 @@ void BlurRelax::buildQuickData(
689
712
rawNeighbors[i] = newNeigh;
690
713
rawHardEdges[i] = newHard;
691
714
rawShiftVal[i] = 0.25 ;
692
- rawShiftComp[i] = 0.75 ;
693
715
}
694
716
}
695
717
@@ -709,7 +731,6 @@ void BlurRelax::buildQuickData(
709
731
710
732
valence.resize (numVertices*4 );
711
733
shiftVal.resize (numVertices*4 );
712
- shiftComp.resize (numVertices*4 );
713
734
714
735
invOrder.resize (order.size ());
715
736
for (size_t i = 0 ; i < order.size (); ++i) {
@@ -734,7 +755,6 @@ void BlurRelax::buildQuickData(
734
755
for (size_t xx = 0 ; xx < 4 ; ++xx) {
735
756
valence[4 * i + xx] = float_t (vale);
736
757
shiftVal[4 * i + xx] = rawShiftVal[order[i]];
737
- shiftComp[4 * i + xx] = rawShiftComp[order[i]];
738
758
}
739
759
}
740
760
delete [] rawVerts;
@@ -746,19 +766,19 @@ void BlurRelax::quickRelax(
746
766
const short hardEdgeBehavior,
747
767
const short groupEdgeBehavior,
748
768
const bool reproject,
749
- const UINT iterations,
769
+ const float taubinBias,
770
+ const float_t iterations,
750
771
const UINT numVerts,
751
772
const std::vector<bool > &group,
752
773
const std::vector<size_t > &order,
753
774
const std::vector<size_t > &invOrder,
754
775
const std::vector<std::vector<size_t >> &neighbors,
755
776
const std::vector<std::vector<bool >> &hardEdges,
756
777
const std::vector<float_t > &shiftVal, // normally 0.5, but it's 0.25 if on a hard edge
757
- const std::vector<float_t > &shiftComp, // normally 0.5, but it's 0.75 if on a hard edge
758
778
const std::vector<float_t > &valence, // as float for vectorizing
759
779
const std::vector<bool > &pinPoints,
760
780
const std::vector<UINT> &creaseCount,
761
- float_t (*verts)[4] // already resized
781
+ float_t (*verts)[4]
762
782
) {
763
783
bool rpEdges = (borderBehavior == BB_SLIDE) || (hardEdgeBehavior == HB_SLIDE) || (groupEdgeBehavior == GB_SLIDE);
764
784
std::vector<size_t > groupIdxs;
@@ -773,6 +793,16 @@ void BlurRelax::quickRelax(
773
793
memcpy (&(baseVerts[0 ][0 ]), &(verts[0 ][0 ]), 4 * numVerts * sizeof (float_t ));
774
794
}
775
795
796
+ float_t (*prevVerts)[4 ];
797
+ prevVerts = new float_t [numVerts][4 ];
798
+
799
+ float_t iterT, iterFI;
800
+ iterT = modf (iterations, &iterFI);
801
+ UINT iterI = (UINT)iterFI;
802
+ if (iterT > 0.0 ) {
803
+ iterI += 1 ;
804
+ }
805
+
776
806
size_t nonzeroValence = neighbors[0 ].size ();
777
807
778
808
MStatus status;
@@ -790,11 +820,20 @@ void BlurRelax::quickRelax(
790
820
octree.create (smoothMesh);
791
821
}
792
822
793
- for (size_t r = 0 ; r < iterations; ++r) {
794
- quickLaplacianSmooth (verts, numVerts, neighbors, valence, shiftVal, shiftComp, pinPoints);
823
+ for (size_t r = 0 ; r < iterI; ++r) {
824
+ if ((r == iterI - 1 ) && (iterT > 0.0 )){
825
+ // Store the next-to-last iteration to interpolate with
826
+ memcpy (&(prevVerts[0 ][0 ]), &(verts[0 ][0 ]), 4 * numVerts * sizeof (float_t ));
827
+ }
828
+ quickLaplacianSmooth (verts, numVerts, neighbors, valence, shiftVal, pinPoints);
829
+ if (taubinBias < 1.0 ){
830
+ quickLaplacianSmooth (verts, numVerts, neighbors, valence, shiftVal, pinPoints, taubinBias);
831
+ }
832
+
795
833
if (rpEdges) {
796
834
edgeProject (baseVerts, groupIdxs, invOrder, neighbors, hardEdges, creaseCount, verts);
797
835
}
836
+
798
837
if (reproject) {
799
838
#pragma omp parallel for if(numVerts>2000)
800
839
for (int i = 0 ; i < nonzeroValence; ++i) {
@@ -812,7 +851,17 @@ void BlurRelax::quickRelax(
812
851
}
813
852
}
814
853
815
- if (rpEdges) delete [] baseVerts;
854
+ // Interpolate between prevVerts and verts based on iterT
855
+ if (iterT > 0.0 ) {
856
+ // This should vectorize
857
+ float_t * vv = &verts[0 ][0 ];
858
+ float_t * pv = &prevVerts[0 ][0 ];
859
+ for (size_t i = 0 ; i < numVerts * 4 ; ++i) {
860
+ vv[i] = ((vv[i] - pv[i]) * iterT) + pv[i];
861
+ }
862
+ }
816
863
864
+ if (rpEdges) delete[] baseVerts;
865
+ delete[] prevVerts;
817
866
}
818
867
0 commit comments