https://github.com/PixarAnimationStudios/OpenSubdiv/blob/release/tutorials/far/tutorial_5_2/far_tutorial_5_2.cpp
using namespace OpenSubdiv;
using Far::Index;
namespace {
struct Pos {
Pos() { }
Pos(float x, float y, float z) { p[0] = x, p[1] = y, p[2] = z; }
Pos operator+(Pos const & op) const {
return Pos(p[0] + op.p[0], p[1] + op.p[1], p[2] + op.p[2]);
}
void Clear( void * =0 ) { p[0] = p[1] = p[2] = 0.0f; }
void AddWithWeight(Pos const & src, float weight) {
p[0] += weight * src.p[0];
p[1] += weight * src.p[1];
p[2] += weight * src.p[2];
}
float p[3];
};
typedef std::vector<Pos> PosVector;
struct Tri {
Tri() { }
Tri(int a, int b, int c) { v[0] = a, v[1] = b, v[2] = c; }
int v[3];
};
typedef std::vector<Tri> TriVector;
void
appendDefaultPrimitive(Pos const & origin,
std::vector<int> & vertsPerFace,
std::vector<Index> & faceVerts,
std::vector<Pos> & positionsPerVert) {
static float const cubePositions[8][3] = { { -0.5f, -0.5f, -0.5f },
{ -0.5f, 0.5f, -0.5f },
{ -0.5f, 0.5f, 0.5f },
{ -0.5f, -0.5f, 0.5f },
{ 0.5f, -0.5f, -0.5f },
{ 0.5f, 0.5f, -0.5f },
{ 0.5f, 0.5f, 0.5f },
{ 0.5f, -0.5f, 0.5f } };
static int const cubeFaceVerts[6][4] = { { 0, 3, 2, 1 },
{ 4, 5, 6, 7 },
{ 0, 4, 7, 3 },
{ 1, 2, 6, 5 },
{ 0, 1, 5, 4 },
{ 3, 7, 6, 2 } };
int baseVertex = (int) positionsPerVert.size();
for (int i = 0; i < 8; ++i) {
float const * p = cubePositions[i];
positionsPerVert.push_back(origin + Pos(p[0], p[1], p[2]));
}
for (int i = 0; i < 6; ++i) {
vertsPerFace.push_back(4);
for (int j = 0; j < 4; ++j) {
faceVerts.push_back(baseVertex + cubeFaceVerts[i][j]);
}
}
}
void
createDefaultGeometry(int multiplier,
std::vector<int> & vertsPerFace,
std::vector<Index> & faceVerts,
std::vector<Pos> & positionsPerVert) {
int const vertsPerPrimitive = 8;
int const facesPerPrimitive = 6;
int const faceVertsPerPrimitive = 24;
int nPrimitives = multiplier * multiplier * multiplier;
positionsPerVert.reserve(nPrimitives * vertsPerPrimitive);
vertsPerFace.reserve(nPrimitives * facesPerPrimitive);
faceVerts.reserve(nPrimitives * faceVertsPerPrimitive);
for (int x = 0; x < multiplier; ++x) {
for (int y = 0; y < multiplier; ++y) {
for (int z = 0; z < multiplier; ++z) {
appendDefaultPrimitive(
Pos((float)x * 2.0f, (float)y * 2.0f, (float)z * 2.0f),
vertsPerFace, faceVerts, positionsPerVert);
}
}
}
}
Far::TopologyRefiner *
createTopologyRefinerDefault(int multiplier,
PosVector & posVector) {
std::vector<int> topVertsPerFace;
std::vector<Index> topFaceVerts;
createDefaultGeometry(
multiplier, topVertsPerFace, topFaceVerts, posVector);
typedef Far::TopologyDescriptor Descriptor;
Sdc::SchemeType type = OpenSubdiv::Sdc::SCHEME_CATMARK;
Sdc::Options options;
options.SetVtxBoundaryInterpolation(
Sdc::Options::VTX_BOUNDARY_EDGE_AND_CORNER);
Descriptor desc;
desc.numVertices = (int) posVector.size();
desc.numFaces = (int) topVertsPerFace.size();
desc.numVertsPerFace = &topVertsPerFace[0];
desc.vertIndicesPerFace = &topFaceVerts[0];
Far::TopologyRefiner * refiner =
Far::TopologyRefinerFactory<Descriptor>::Create(desc,
Far::TopologyRefinerFactory<Descriptor>::Options(
type, options));
if (refiner == 0) {
exit(EXIT_FAILURE);
}
bool dumpDefaultGeometryToObj = false;
if (dumpDefaultGeometryToObj) {
int nVerts = (int) posVector.size();
for (int i = 0; i < nVerts; ++i) {
float const * p = posVector[i].p;
printf("v %f %f %f\n", p[0], p[1], p[2]);
}
int const * fVerts = &topFaceVerts[0];
int nFaces = (int) topVertsPerFace.size();
for (int i = 0; i < nFaces; ++i) {
printf("f");
for (int j = 0; j < topVertsPerFace[i]; ++j) {
printf(" %d", 1 + *fVerts++);
}
printf("\n");
}
exit(EXIT_SUCCESS);
}
return refiner;
}
Far::TopologyRefiner *
createTopologyRefinerFromObj(std::string const & objFileName,
Sdc::SchemeType schemeType,
PosVector & posVector) {
const char * filename = objFileName.c_str();
const Shape * shape = 0;
std::ifstream ifs(filename);
if (ifs) {
std::stringstream ss;
ss << ifs.rdbuf();
ifs.close();
std::string shapeString = ss.str();
shape = Shape::parseObj(shapeString.c_str(),
ConvertSdcTypeToShapeScheme(schemeType), false);
if (shape == 0) {
fprintf(stderr, "Error: Cannot create Shape "
"from .obj file '%s'\n", filename);
return 0;
}
} else {
fprintf(stderr, "Error: Cannot open .obj file '%s'\n", filename);
return 0;
}
Sdc::SchemeType sdcType = GetSdcType(*shape);
Sdc::Options sdcOptions = GetSdcOptions(*shape);
Far::TopologyRefiner * refiner =
Far::TopologyRefinerFactory<Shape>::Create(*shape,
Far::TopologyRefinerFactory<Shape>::Options(
sdcType, sdcOptions));
if (refiner == 0) {
fprintf(stderr, "Error: Unable to construct TopologyRefiner "
"from .obj file '%s'\n", filename);
return 0;
}
int numVertices = refiner->GetNumVerticesTotal();
posVector.resize(numVertices);
std::memcpy(&posVector[0].p[0], &shape->verts[0],
numVertices * 3 * sizeof(float));
delete shape;
return refiner;
}
}
struct PatchGroup {
PatchGroup(Far::PatchTableFactory::Options patchOptions,
Far::TopologyRefiner const & baseRefinerArg,
Far::PtexIndices const & basePtexIndicesArg,
std::vector<Pos> const & basePositionsArg,
std::vector<Index> const & baseFacesArg);
~PatchGroup();
void TessellateBaseFace(int face, PosVector & tessPoints,
TriVector & tessTris) const;
Far::TopologyRefiner const & baseRefiner;
Far::PtexIndices const & basePtexIndices;
std::vector<Pos> const & basePositions;
std::vector<Index> const & baseFaces;
Far::PatchTable * patchTable;
Far::PatchMap * patchMap;
int patchFaceSize;
std::vector<Pos> localPositions;
};
PatchGroup::PatchGroup(Far::PatchTableFactory::Options patchOptions,
Far::TopologyRefiner const & baseRefinerArg,
Far::PtexIndices const & basePtexIndicesArg,
std::vector<Pos> const & basePositionsArg,
std::vector<Index> const & baseFacesArg) :
baseRefiner(baseRefinerArg),
basePtexIndices(basePtexIndicesArg),
basePositions(basePositionsArg),
baseFaces(baseFacesArg) {
Far::ConstIndexArray groupFaces(&baseFaces[0], (int)baseFaces.size());
Far::TopologyRefiner *localRefiner =
Far::TopologyRefinerFactory<Far::TopologyDescriptor>::Create(
baseRefiner);
localRefiner->RefineAdaptive(
patchOptions.GetRefineAdaptiveOptions(), groupFaces);
patchTable = Far::PatchTableFactory::Create(*localRefiner, patchOptions,
groupFaces);
patchMap = new Far::PatchMap(*patchTable);
patchFaceSize =
Sdc::SchemeTypeTraits::GetRegularFaceSize(baseRefiner.GetSchemeType());
int nBaseVertices = localRefiner->GetLevel(0).GetNumVertices();
int nRefinedVertices = localRefiner->GetNumVerticesTotal() - nBaseVertices;
int nLocalPoints = patchTable->GetNumLocalPoints();
localPositions.resize(nRefinedVertices + nLocalPoints);
if (nRefinedVertices) {
Far::PrimvarRefiner primvarRefiner(*localRefiner);
Pos const * src = &basePositions[0];
Pos * dst = &localPositions[0];
for (int level = 1; level < localRefiner->GetNumLevels(); ++level) {
primvarRefiner.Interpolate(level, src, dst);
src = dst;
dst += localRefiner->GetLevel(level).GetNumVertices();
}
}
if (nLocalPoints) {
patchTable->GetLocalPointStencilTable()->UpdateValues(
&basePositions[0], nBaseVertices, &localPositions[0],
&localPositions[nRefinedVertices]);
}
delete localRefiner;
}
PatchGroup::~PatchGroup() {
delete patchTable;
delete patchMap;
}
void
PatchGroup::TessellateBaseFace(int face, PosVector & tessPoints,
TriVector & tessTris) const {
float const quadPoints[5][2] = { { 0.5f, 0.5f },
{ 0.0f, 0.0f },
{ 1.0f, 0.0f },
{ 1.0f, 1.0f },
{ 0.0f, 1.0f } };
float const triPoints[4][2] = { { 0.5f, 0.5f },
{ 0.0f, 0.0f },
{ 1.0f, 0.0f },
{ 0.0f, 1.0f } };
float const irregPoints[4][2] = { { 1.0f, 1.0f },
{ 0.0f, 0.0f } };
int baseFace = baseFaces[face];
int faceSize = baseRefiner.GetLevel(0).GetFaceVertices(baseFace).size();
bool faceIsIrregular = (faceSize != patchFaceSize);
int nTessPoints = faceSize + 1;
int nTessFaces = faceSize;
tessPoints.resize(nTessPoints);
tessTris.resize(nTessFaces);
int ptexFace = basePtexIndices.GetFaceId(baseFace);
int numBaseVerts = (int) basePositions.size();
for (int i = 0; i < nTessPoints; ++i) {
float const * st = faceIsIrregular ? irregPoints[i != 0]
: ((faceSize == 4) ? quadPoints[i] : triPoints[i]);
int patchFace = ptexFace;
if (faceIsIrregular && (i > 0)) {
patchFace += i - 1;
}
Far::PatchTable::PatchHandle const * handle =
patchMap->FindPatch(patchFace, st[0], st[1]);
assert(handle);
float pWeights[20];
patchTable->EvaluateBasis(*handle, st[0], st[1], pWeights);
Far::ConstIndexArray cvIndices = patchTable->GetPatchVertices(*handle);
Pos & pos = tessPoints[i];
pos.Clear();
for (int cv = 0; cv < cvIndices.size(); ++cv) {
int cvIndex = cvIndices[cv];
if (cvIndex < numBaseVerts) {
pos.AddWithWeight(basePositions[cvIndex],
pWeights[cv]);
} else {
pos.AddWithWeight(localPositions[cvIndex - numBaseVerts],
pWeights[cv]);
}
}
}
for (int i = 0; i < nTessFaces; ++i) {
tessTris[i] = Tri(0, 1 + i, 1 + ((i + 1) % faceSize));
}
}
class Args {
public:
std::string inputObjFile;
Sdc::SchemeType schemeType;
int geoMultiplier;
int maxPatchDepth;
int numPatchGroups;
bool noTessFlag;
bool noOutputFlag;
public:
Args(int argc, char ** argv) :
inputObjFile(),
schemeType(Sdc::SCHEME_CATMARK),
geoMultiplier(10),
maxPatchDepth(3),
numPatchGroups(10),
noTessFlag(false),
noOutputFlag(false) {
ArgOptions args;
args.Parse(argc, argv);
maxPatchDepth = args.GetLevel();
schemeType = ConvertShapeSchemeToSdcType(args.GetDefaultScheme());
const std::vector<const char *> objFiles = args.GetObjFiles();
if (!objFiles.empty()) {
for (size_t i = 1; i < objFiles.size(); ++i) {
fprintf(stderr,
"Warning: .obj file '%s' ignored\n", objFiles[i]);
}
inputObjFile = std::string(objFiles[0]);
}
const std::vector<const char *> &rargs = args.GetRemainingArgs();
for (size_t i = 0; i < rargs.size(); ++i) {
if (!strcmp(rargs[i], "-groups")) {
if (++i < rargs.size()) numPatchGroups = atoi(rargs[i]);
} else if (!strcmp(rargs[i], "-mult")) {
if (++i < rargs.size()) geoMultiplier = atoi(rargs[i]);
} else if (!strcmp(rargs[i], "-notess")) {
noTessFlag = true;
} else if (!strcmp(rargs[i], "-nooutput")) {
noOutputFlag = true;
} else {
fprintf(stderr, "Warning: Argument '%s' ignored\n", rargs[i]);
}
}
}
private:
Args() { }
};
int
main(int argc, char **argv) {
Args args(argc, argv);
std::vector<Pos> basePositions;
Far::TopologyRefiner * baseRefinerPtr = args.inputObjFile.empty() ?
createTopologyRefinerDefault(args.geoMultiplier, basePositions) :
createTopologyRefinerFromObj(args.inputObjFile, args.schemeType,
basePositions);
assert(baseRefinerPtr);
Far::TopologyRefiner & baseRefiner = *baseRefinerPtr;
Far::PtexIndices basePtexIndices(baseRefiner);
int numBaseFaces = baseRefiner.GetNumFacesTotal();
int numPatchGroups = args.numPatchGroups;
if (numPatchGroups > numBaseFaces) {
numPatchGroups = numBaseFaces;
} else if (numPatchGroups < 1) {
numPatchGroups = 1;
}
int lesserGroupSize = numBaseFaces / numPatchGroups;
int numLargerGroups = numBaseFaces - (numPatchGroups * lesserGroupSize);
Far::PatchTableFactory::Options patchOptions(args.maxPatchDepth);
patchOptions.generateVaryingTables = false;
patchOptions.shareEndCapPatchPoints = false;
patchOptions.endCapType =
Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
int objVertCount = 0;
PosVector tessPoints;
TriVector tessFaces;
for (int i = 0; i < numPatchGroups; ++i) {
Index minFace = i * lesserGroupSize + std::min(i, numLargerGroups);
Index maxFace = minFace + lesserGroupSize + (i < numLargerGroups);
std::vector<Far::Index> baseFaces(maxFace - minFace);
for (int face = minFace; face < maxFace; ++face) {
baseFaces[face - minFace] = face;
}
PatchGroup patchGroup(patchOptions,
baseRefiner, basePtexIndices, basePositions, baseFaces);
if (args.noTessFlag) continue;
if (!args.noOutputFlag) {
printf("g patchGroup_%d\n", i);
}
for (int j = 0; j < (int) baseFaces.size(); ++j) {
patchGroup.TessellateBaseFace(j, tessPoints, tessFaces);
if (!args.noOutputFlag) {
int nVerts = (int) tessPoints.size();
for (int k = 0; k < nVerts; ++k) {
float const * p = tessPoints[k].p;
printf("v %f %f %f\n", p[0], p[1], p[2]);
}
int nTris = (int) tessFaces.size();
int vBase = 1 + objVertCount;
for (int k = 0; k < nTris; ++k) {
int const * v = tessFaces[k].v;
printf("f %d %d %d\n",
vBase + v[0], vBase + v[1], vBase + v[2]);
}
objVertCount += nVerts;
}
}
}
delete baseRefinerPtr;
return EXIT_SUCCESS;
}