https://github.com/PixarAnimationStudios/OpenSubdiv/blob/release/tutorials/bfr/tutorial_3_2/bfr_tutorial_3_2.cpp
using namespace OpenSubdiv;
class Args {
public:
std::string inputObjFile;
std::string outputObjFile;
Sdc::SchemeType schemeType;
int tessUniformRate;
bool tessQuadsFlag;
public:
Args(int argc, char * argv[]) :
inputObjFile(),
outputObjFile(),
schemeType(Sdc::SCHEME_CATMARK),
tessUniformRate(5),
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], "-res")) {
if (++i < argc) tessUniformRate = atoi(argv[i]);
} else if (!strcmp(argv[i], "-quads")) {
tessQuadsFlag = true;
} else {
fprintf(stderr,
"Warning: Unrecognized argument '%s' ignored\n", argv[i]);
}
}
}
private:
Args() { }
};
class SurfaceCache {
public:
typedef Bfr::Surface<float> Surface;
typedef Bfr::RefinerSurfaceFactory<> SurfaceFactory;
public:
SurfaceCache(SurfaceFactory const & surfaceFactory,
std::vector<float> const & meshPoints,
bool cachePatchPoints = true,
bool cacheAllSurfaces = true);
SurfaceCache() = delete;
~SurfaceCache() = default;
bool FaceHasLimitSurface(int face) { return _entries[face].hasLimit; }
Surface const * GetSurface(int face) { return _entries[face].surface.get();}
float const * GetPatchPoints(int face) { return getPatchPoints(face); }
private:
struct FaceEntry {
FaceEntry() : surface(), hasLimit(false), pointOffset(-1) { }
std::unique_ptr<Surface const> surface;
bool hasLimit;
int pointOffset;
};
float * getPatchPoints(int face) {
return (_entries[face].surface && !_points.empty()) ?
(_points.data() + _entries[face].pointOffset * 3) : 0;
}
private:
std::vector<FaceEntry> _entries;
std::vector<float> _points;
};
SurfaceCache::SurfaceCache(SurfaceFactory const & surfaceFactory,
std::vector<float> const & meshPoints,
bool cachePatchPoints,
bool cacheAllSurfaces) {
int numFaces = surfaceFactory.GetNumFaces();
_entries.resize(numFaces);
int numPointsInCache = 0;
for (int face = 0; face < numFaces; ++face) {
Surface * s = surfaceFactory.CreateVertexSurface<float>(face);
if (s) {
FaceEntry & entry = _entries[face];
entry.hasLimit = true;
if (cacheAllSurfaces || (!s->IsRegular() && !s->IsLinear())) {
entry.surface.reset(s);
entry.pointOffset = numPointsInCache;
numPointsInCache += s->GetNumPatchPoints();
} else {
delete s;
}
}
}
if (cachePatchPoints) {
_points.resize(numPointsInCache * 3);
for (int face = 0; face < numFaces; ++face) {
float * patchPoints = getPatchPoints(face);
if (patchPoints) {
GetSurface(face)->PreparePatchPoints(meshPoints.data(), 3,
patchPoints, 3);
}
}
}
}
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);
bool cachePatchPoints = true;
bool cacheAllSurfaces = true;
SurfaceCache surfaceCache(meshSurfaceFactory, meshVertexPositions,
cachePatchPoints, cacheAllSurfaces);
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 (!surfaceCache.FaceHasLimitSurface(faceIndex)) continue;
Surface const & faceSurface = * surfaceCache.GetSurface(faceIndex);
Bfr::Tessellation tessPattern(faceSurface.GetParameterization(),
options.tessUniformRate, tessOptions);
int numOutCoords = tessPattern.GetNumCoords();
outCoords.resize(numOutCoords * 2);
tessPattern.GetCoords(outCoords.data());
float const * facePatchPoints = surfaceCache.GetPatchPoints(faceIndex);
int pointSize = 3;
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, 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;
}
tessellateToObj(*meshTopology, meshVtxPositions, args);
delete meshTopology;
return EXIT_SUCCESS;
}