Skip to content

Commit c7892be

Browse files
authored
Merge pull request #6 from tbttfox/taubin
Implement Taubin Smoothing
2 parents b443477 + d2d6031 commit c7892be

File tree

1 file changed

+86
-37
lines changed

1 file changed

+86
-37
lines changed

blurRelax.cpp

Lines changed: 86 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ SOFTWARE.
5757
#include <vector>
5858
#include <algorithm>
5959
#include <numeric>
60+
#include <math.h>
6061

6162
#define CHECKSTAT(m) if (!status) {status.perror(m); return status;};
6263

@@ -72,13 +73,17 @@ SOFTWARE.
7273
#define GB_PIN 1
7374
#define GB_SLIDE 2
7475

76+
#define SA_LAPLACIAN 0
77+
#define SA_TAUBIN 1
78+
7579
#define DEFORMER_NAME "BlurRelax"
7680

7781
// Double vs Float
7882
#define float_t double
7983
#define point_t MPoint
8084
#define pointArray_t MPointArray
8185

86+
8287
void edgeProject(
8388
const float_t basePoints[][4],
8489
const std::vector<size_t> &group,
@@ -174,8 +179,8 @@ void quickLaplacianSmooth(
174179
const std::vector<std::vector<size_t>> &neighbors,
175180
const std::vector<float_t> &valence,
176181
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
179184
) {
180185
/*
181186
All the crazy hoops I've jumped through are to make auto-vectorization work
@@ -184,23 +189,19 @@ void quickLaplacianSmooth(
184189
of a std::vector of neighbors per vert with that vector sorted so the verts
185190
with the most neighbors were at the top.
186191
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
193192
*/
194193

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]
196195
float_t* verts = &(verts2d[0][0]);
197196

198197
// number of nonzero valence
199198
size_t nzv = neighbors[0].size();
199+
200+
// number of nonzero components
200201
size_t nzc = 4 * nzv;
201202

202203
// 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
204205
// This allows for auto-vectorization
205206
float_t * __restrict outComp = new float_t[nzc];
206207
memset(outComp, 0, nzc*sizeof(float_t));
@@ -216,10 +217,8 @@ void quickLaplacianSmooth(
216217
}
217218
}
218219

219-
// Depending on the compiler optimization, it may be faster to break up this line
220-
// Gotta test
221220
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];
223222
}
224223

225224
memcpy(verts, outComp, nzc*sizeof(float_t));
@@ -244,7 +243,7 @@ class BlurRelax : public MPxDeformerNode {
244243
static MObject aHardEdgeBehavior;
245244
static MObject aGroupEdgeBehavior;
246245
static MObject aReproject;
247-
246+
static MObject aTaubinBias;
248247
static MTypeId id;
249248
private:
250249

@@ -269,7 +268,6 @@ class BlurRelax : public MPxDeformerNode {
269268
std::vector<std::vector<size_t>> &neighbors,
270269
std::vector<std::vector<bool>> &hardEdges,
271270
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
273271
std::vector<float_t> &valence, // as float for vectorizing
274272
std::vector<bool> &pinPoints,
275273
std::vector<UINT> &creaseCount,
@@ -282,15 +280,15 @@ class BlurRelax : public MPxDeformerNode {
282280
const short hardEdgeBehavior,
283281
const short groupEdgeBehavior,
284282
const bool reproject,
285-
const UINT iterations,
283+
const float taubinBias,
284+
const float_t iterations,
286285
const UINT numVerts,
287286
const std::vector<bool> &group,
288287
const std::vector<size_t> &order,
289288
const std::vector<size_t> &invOrder,
290289
const std::vector<std::vector<size_t>> &neighbors,
291290
const std::vector<std::vector<bool>> &hardEdges,
292291
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
294292
const std::vector<float_t> &valence, // as float for vectorizing
295293
const std::vector<bool> &pinPoints,
296294
const std::vector<UINT> &creaseCount,
@@ -306,6 +304,8 @@ MObject BlurRelax::aBorderBehavior;
306304
MObject BlurRelax::aHardEdgeBehavior;
307305
MObject BlurRelax::aGroupEdgeBehavior;
308306
MObject BlurRelax::aReproject;
307+
MObject BlurRelax::aTaubinBias;
308+
309309

310310
BlurRelax::BlurRelax() {}
311311
BlurRelax::~BlurRelax() {}
@@ -423,9 +423,20 @@ MStatus BlurRelax::initialize() {
423423
status = attributeAffects(aReproject, outputGeom);
424424
CHECKSTAT("aReproject");
425425

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);
427438
CHECKSTAT("aIterations");
428-
nAttr.setMin(0);
439+
nAttr.setMin(0.0);
429440
nAttr.setChannelBox(true);
430441
status = addAttribute(aIterations);
431442
CHECKSTAT("aIterations");
@@ -444,7 +455,7 @@ MStatus BlurRelax::deform(MDataBlock& dataBlock, MItGeometry& vertIter, const MM
444455
float env = hEnv.asFloat();
445456

446457
MDataHandle hIter = dataBlock.inputValue(aIterations);
447-
int iterations = hIter.asInt();
458+
float iterations = hIter.asFloat();
448459

449460
if (iterations > 0 && env > 0.0f){
450461
// Get the data from the node
@@ -457,6 +468,23 @@ MStatus BlurRelax::deform(MDataBlock& dataBlock, MItGeometry& vertIter, const MM
457468
MDataHandle hReproject = dataBlock.inputValue(aReproject);
458469
bool reproject = hReproject.asBool();
459470

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+
460488
// get the input mesh corresponding to this output
461489
MObject thisNode = this->thisMObject();
462490
MPlug inPlug(thisNode, input);
@@ -471,7 +499,6 @@ MStatus BlurRelax::deform(MDataBlock& dataBlock, MItGeometry& vertIter, const MM
471499
std::vector<std::vector<size_t>> neighbors;
472500
std::vector<std::vector<bool>> hardEdges;
473501
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
475502
std::vector<float_t> valence; // as float for vectorizing
476503
std::vector<bool> pinPoints;
477504
std::vector<UINT> creaseCount;
@@ -489,7 +516,7 @@ MStatus BlurRelax::deform(MDataBlock& dataBlock, MItGeometry& vertIter, const MM
489516
// Populate the variables with *SPECIALLY ORDERED* data
490517
// all vertex data is now shuffled by the order vector
491518
buildQuickData(mesh, vertIter, bb, hb, gb, reproject,
492-
group, order, invOrder, neighbors, hardEdges, shiftVal, shiftComp, valence,
519+
group, order, invOrder, neighbors, hardEdges, shiftVal, valence,
493520
pinPoints, creaseCount, verts);
494521

495522
// This can happen if the user is pinning all the points
@@ -500,9 +527,9 @@ MStatus BlurRelax::deform(MDataBlock& dataBlock, MItGeometry& vertIter, const MM
500527
}
501528

502529
// Calculate the relax, and store in verts
503-
quickRelax(mesh, bb, hb, gb, reproject,
530+
quickRelax(mesh, bb, hb, gb, reproject, tBias,
504531
iterations, numVerts, group, order, invOrder, neighbors, hardEdges, shiftVal,
505-
shiftComp, valence, pinPoints, creaseCount, verts);
532+
valence, pinPoints, creaseCount, verts);
506533

507534
// Get the painted weight values
508535
std::vector<float> weightVals;
@@ -548,7 +575,6 @@ void BlurRelax::buildQuickData(
548575
std::vector<std::vector<size_t>> &neighbors,
549576
std::vector<std::vector<bool>> &hardEdges,
550577
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
552578
std::vector<float_t> &valence, // as float for vectorizing
553579
std::vector<bool> &pinPoints,
554580
std::vector<UINT> &creaseCount,
@@ -665,11 +691,8 @@ void BlurRelax::buildQuickData(
665691
// if a vert has a pinned neighbor, remove it
666692

667693
std::vector<float_t> rawShiftVal;
668-
std::vector<float_t> rawShiftComp;
669694
rawShiftVal.resize(numVertices);
670-
rawShiftComp.resize(numVertices);
671695
std::fill(rawShiftVal.begin(), rawShiftVal.end(), 0.5);
672-
std::fill(rawShiftComp.begin(), rawShiftComp.end(), 0.5);
673696

674697
for (size_t i = 0; i < rawNeighbors.size(); ++i) {
675698
if ((rawCreaseCount[i] != 0) || rawPinPoints[i]) {
@@ -689,7 +712,6 @@ void BlurRelax::buildQuickData(
689712
rawNeighbors[i] = newNeigh;
690713
rawHardEdges[i] = newHard;
691714
rawShiftVal[i] = 0.25;
692-
rawShiftComp[i] = 0.75;
693715
}
694716
}
695717

@@ -709,7 +731,6 @@ void BlurRelax::buildQuickData(
709731

710732
valence.resize(numVertices*4);
711733
shiftVal.resize(numVertices*4);
712-
shiftComp.resize(numVertices*4);
713734

714735
invOrder.resize(order.size());
715736
for (size_t i = 0; i < order.size(); ++i) {
@@ -734,7 +755,6 @@ void BlurRelax::buildQuickData(
734755
for (size_t xx = 0; xx < 4; ++xx) {
735756
valence[4 * i + xx] = float_t(vale);
736757
shiftVal[4 * i + xx] = rawShiftVal[order[i]];
737-
shiftComp[4 * i + xx] = rawShiftComp[order[i]];
738758
}
739759
}
740760
delete [] rawVerts;
@@ -746,19 +766,19 @@ void BlurRelax::quickRelax(
746766
const short hardEdgeBehavior,
747767
const short groupEdgeBehavior,
748768
const bool reproject,
749-
const UINT iterations,
769+
const float taubinBias,
770+
const float_t iterations,
750771
const UINT numVerts,
751772
const std::vector<bool> &group,
752773
const std::vector<size_t> &order,
753774
const std::vector<size_t> &invOrder,
754775
const std::vector<std::vector<size_t>> &neighbors,
755776
const std::vector<std::vector<bool>> &hardEdges,
756777
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
758778
const std::vector<float_t> &valence, // as float for vectorizing
759779
const std::vector<bool> &pinPoints,
760780
const std::vector<UINT> &creaseCount,
761-
float_t(*verts)[4] // already resized
781+
float_t(*verts)[4]
762782
) {
763783
bool rpEdges = (borderBehavior == BB_SLIDE) || (hardEdgeBehavior == HB_SLIDE) || (groupEdgeBehavior == GB_SLIDE);
764784
std::vector<size_t> groupIdxs;
@@ -773,6 +793,16 @@ void BlurRelax::quickRelax(
773793
memcpy(&(baseVerts[0][0]), &(verts[0][0]), 4 * numVerts * sizeof(float_t));
774794
}
775795

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+
776806
size_t nonzeroValence = neighbors[0].size();
777807

778808
MStatus status;
@@ -790,11 +820,20 @@ void BlurRelax::quickRelax(
790820
octree.create(smoothMesh);
791821
}
792822

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+
795833
if (rpEdges) {
796834
edgeProject(baseVerts, groupIdxs, invOrder, neighbors, hardEdges, creaseCount, verts);
797835
}
836+
798837
if (reproject) {
799838
#pragma omp parallel for if(numVerts>2000)
800839
for (int i = 0; i < nonzeroValence; ++i) {
@@ -812,7 +851,17 @@ void BlurRelax::quickRelax(
812851
}
813852
}
814853

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+
}
816863

864+
if (rpEdges) delete[] baseVerts;
865+
delete[] prevVerts;
817866
}
818867

0 commit comments

Comments
 (0)