https://github.com/PixarAnimationStudios/OpenSubdiv/blob/release/tutorials/bfr/tutorial_2_1/bfr_tutorial_2_1.cpp
using namespace OpenSubdiv;
class Args {
public:
std::string inputObjFile;
std::string outputObjFile;
Sdc::SchemeType schemeType;
float tessInterval;
int tessRateMax;
bool useHullFlag;
bool tessQuadsFlag;
public:
Args(int argc, char * argv[]) :
inputObjFile(),
outputObjFile(),
schemeType(Sdc::SCHEME_CATMARK),
tessInterval(0.0f),
tessRateMax(10),
useHullFlag(false),
tessQuadsFlag(false) {
for (int i = 1; i < argc; ++i) {
if (strstr(argv[i], ".obj")) {
if (inputObjFile.empty()) {
inputObjFile = std::string(argv[i]);
} else {
fprintf(stderr,
"Warning: Extra Obj file '%s' ignored\n", argv[i]);
}
} else if (!strcmp(argv[i], "-o")) {
if (++i < argc) outputObjFile = std::string(argv[i]);
} else if (!strcmp(argv[i], "-bilinear")) {
schemeType = Sdc::SCHEME_BILINEAR;
} else if (!strcmp(argv[i], "-catmark")) {
schemeType = Sdc::SCHEME_CATMARK;
} else if (!strcmp(argv[i], "-loop")) {
schemeType = Sdc::SCHEME_LOOP;
} else if (!strcmp(argv[i], "-length")) {
if (++i < argc) tessInterval = (float) atof(argv[i]);
} else if (!strcmp(argv[i], "-max")) {
if (++i < argc) tessRateMax = atoi(argv[i]);
} else if (!strcmp(argv[i], "-hull")) {
useHullFlag = true;
} else if (!strcmp(argv[i], "-quads")) {
tessQuadsFlag = true;
} else {
fprintf(stderr,
"Warning: Unrecognized argument '%s' ignored\n", argv[i]);
}
}
}
private:
Args() { }
};
inline float
EdgeLength(float const * v0, float const * v1) {
float dv[3];
dv[0] = std::abs(v0[0] - v1[0]);
dv[1] = std::abs(v0[1] - v1[1]);
dv[2] = std::abs(v0[2] - v1[2]);
return std::sqrt(dv[0]*dv[0] + dv[1]*dv[1] + dv[2]*dv[2]);
}
float
FindLongestEdge(Far::TopologyRefiner const & mesh,
std::vector<float> const & vertPos, int pointSize) {
float maxLength = 0.0f;
int numEdges = mesh.GetLevel(0).GetNumEdges();
for (int i = 0; i < numEdges; ++i) {
Far::ConstIndexArray edgeVerts = mesh.GetLevel(0).GetEdgeVertices(i);
float edgeLength = EdgeLength(&vertPos[edgeVerts[0] * pointSize],
&vertPos[edgeVerts[1] * pointSize]);
maxLength = std::max(maxLength, edgeLength);
}
return maxLength;
}
void
GetEdgeTessRates(std::vector<float> const & vertPos, int pointSize,
Args const & options,
int * edgeRates) {
int numEdges = (int) vertPos.size() / pointSize;
for (int i = 0; i < numEdges; ++i) {
int j = (i + 1) % numEdges;
float edgeLength = EdgeLength(&vertPos[i * pointSize],
&vertPos[j * pointSize]);
edgeRates[i] = 1 + (int)(edgeLength / options.tessInterval);
edgeRates[i] = std::min(edgeRates[i], options.tessRateMax);
}
}
void
tessellateToObj(Far::TopologyRefiner const & meshTopology,
std::vector<float> const & meshVertexPositions,
Args const & options) {
typedef Bfr::RefinerSurfaceFactory<> SurfaceFactory;
typedef Bfr::Surface<float> Surface;
SurfaceFactory::Options surfaceOptions;
SurfaceFactory meshSurfaceFactory(meshTopology, surfaceOptions);
Surface faceSurface;
std::vector<float> facePatchPoints;
std::vector<int> faceTessRates;
std::vector<float> outCoords;
std::vector<float> outPos, outDu, outDv;
std::vector<int> outFacets;
int const tessFacetSize = 3 + options.tessQuadsFlag;
Bfr::Tessellation::Options tessOptions;
tessOptions.SetFacetSize(tessFacetSize);
tessOptions.PreserveQuads(options.tessQuadsFlag);
tutorial::ObjWriter objWriter(options.outputObjFile);
int numFaces = meshSurfaceFactory.GetNumFaces();
for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
if (!meshSurfaceFactory.InitVertexSurface(faceIndex, &faceSurface)) {
continue;
}
int pointSize = 3;
facePatchPoints.resize(faceSurface.GetNumPatchPoints() * pointSize);
faceSurface.PreparePatchPoints(meshVertexPositions.data(), pointSize,
facePatchPoints.data(), pointSize);
int N = faceSurface.GetFaceSize();
outPos.resize(N * pointSize);
if (options.useHullFlag) {
Far::ConstIndexArray verts =
meshTopology.GetLevel(0).GetFaceVertices(faceIndex);
for (int i = 0, j = 0; i < N; ++i, j += pointSize) {
float const * vPos = &meshVertexPositions[verts[i] * pointSize];
outPos[j ] = vPos[0];
outPos[j+1] = vPos[1];
outPos[j+2] = vPos[2];
}
} else {
Bfr::Parameterization faceParam = faceSurface.GetParameterization();
for (int i = 0, j = 0; i < N; ++i, j += pointSize) {
float uv[2];
faceParam.GetVertexCoord(i, uv);
faceSurface.Evaluate(uv, facePatchPoints.data(), pointSize,
&outPos[j]);
}
}
faceTessRates.resize(N);
GetEdgeTessRates(outPos, pointSize, options, faceTessRates.data());
Bfr::Tessellation tessPattern(faceSurface.GetParameterization(),
N, faceTessRates.data(), tessOptions);
int numOutCoords = tessPattern.GetNumCoords();
outCoords.resize(numOutCoords * 2);
tessPattern.GetCoords(outCoords.data());
outPos.resize(numOutCoords * pointSize);
outDu.resize(numOutCoords * pointSize);
outDv.resize(numOutCoords * pointSize);
for (int i = 0, j = 0; i < numOutCoords; ++i, j += pointSize) {
faceSurface.Evaluate(&outCoords[i*2],
facePatchPoints.data(), pointSize,
&outPos[j], &outDu[j], &outDv[j]);
}
int objVertexIndexOffset = objWriter.GetNumVertices();
int numFacets = tessPattern.GetNumFacets();
outFacets.resize(numFacets * tessFacetSize);
tessPattern.GetFacets(outFacets.data());
tessPattern.TransformFacetCoordIndices(outFacets.data(),
objVertexIndexOffset);
objWriter.WriteGroupName("baseFace_", faceIndex);
objWriter.WriteVertexPositions(outPos);
objWriter.WriteVertexNormals(outDu, outDv);
objWriter.WriteFaces(outFacets, tessFacetSize, true, false);
}
}
int
main(int argc, char * argv[]) {
Args args(argc, argv);
Far::TopologyRefiner * meshTopology = 0;
std::vector<float> meshVtxPositions;
std::vector<float> meshFVarUVs;
meshTopology = tutorial::createTopologyRefiner(
args.inputObjFile, args.schemeType, meshVtxPositions, meshFVarUVs);
if (meshTopology == 0) {
return EXIT_FAILURE;
}
if (args.tessInterval <= 0.0f) {
args.tessInterval = FindLongestEdge(*meshTopology, meshVtxPositions, 3)
/ (float) args.tessRateMax;
}
tessellateToObj(*meshTopology, meshVtxPositions, args);
delete meshTopology;
return EXIT_SUCCESS;
}