bfr_tutorial_1_1.cpp
System Message: WARNING/2 (/build/opensubdiv/src/OpenSubdiv-3_6_1/build/documentation/bfr_tutorial_1_1.rst, line 9)
Cannot analyze code. Pygments package not found.
.. code:: c++
//------------------------------------------------------------------------------
// Tutorial description:
//
// This tutorial illustrates the use of the SurfaceFactory, Surface
// and Parameterization classes for creating and evaluating the limit
// surface associated with each base face of a mesh.
//
// Following the creation of a connected mesh for a shape (using a
// Far::TopologyRefiner, as illustrated in Far tutorials), an instance
// of a SurfaceFactory is declared to process its faces. Each face of
// the mesh is evaluated and tessellated independently (with a simple
// triangle fan), with results written out in Obj format for inspection.
//
// These classes make it simple to evaluate and tessellate all faces
// (quads, tris or others) while supporting the full set of subdivision
// options. While a triangle fan may be a trivial tessellation (and so
// not very useful) later examples using the Tessellation class provide
// more useful results with the same simplicity.
//
#include <opensubdiv/far/topologyRefiner.h>
#include <opensubdiv/bfr/refinerSurfaceFactory.h>
#include <opensubdiv/bfr/surface.h>
#include <vector>
#include <string>
#include <cstring>
#include <cstdio>
// Local headers with support for this tutorial in "namespace tutorial"
#include "./meshLoader.h"
#include "./objWriter.h"
using namespace OpenSubdiv;
//
// Simple command line arguments to provide input and run-time options:
//
class Args {
public:
std::string inputObjFile;
std::string outputObjFile;
Sdc::SchemeType schemeType;
public:
Args(int argc, char * argv[]) :
inputObjFile(),
outputObjFile(),
schemeType(Sdc::SCHEME_CATMARK) {
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 {
fprintf(stderr,
"Warning: Unrecognized argument '%s' ignored\n", argv[i]);
}
}
}
private:
Args() { }
};
//
// The main tessellation function: given a mesh and vertex positions,
// tessellate each face -- writing results in Obj format.
//
void
tessellateToObj(Far::TopologyRefiner const & meshTopology,
std::vector<float> const & meshVertexPositions,
Args const & options) {
//
// Use simpler local type names for the Surface and its factory:
//
typedef Bfr::RefinerSurfaceFactory<> SurfaceFactory;
typedef Bfr::Surface<float> Surface;
//
// Initialize the SurfaceFactory for the given base mesh (very low
// cost in terms of both time and space) and tessellate each face
// independently (i.e. no shared vertices):
//
// Note that the SurfaceFactory is not thread-safe by default due to
// use of an internal cache. Creating a separate instance of the
// SurfaceFactory for each thread is one way to safely parallelize
// this loop. Another (preferred) is to assign a thread-safe cache
// to the single instance.
//
// First declare any evaluation options when initializing (though
// none are used in this simple case):
//
SurfaceFactory::Options surfaceOptions;
SurfaceFactory meshSurfaceFactory(meshTopology, surfaceOptions);
//
// The Surface to be constructed and evaluated for each face -- as
// well as the intermediate and output data associated with it -- can
// be declared in the scope local to each face. But since dynamic
// memory is involved with these variables, it is preferred to declare
// them outside that loop to preserve and reuse that dynamic memory.
//
Surface faceSurface;
std::vector<float> facePatchPoints;
std::vector<float> outCoords;
std::vector<float> outPos, outDu, outDv;
std::vector<int> outTriangles;
//
// Process each face, writing the output of each in Obj format:
//
tutorial::ObjWriter objWriter(options.outputObjFile);
int numFaces = meshSurfaceFactory.GetNumFaces();
for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
//
// Initialize the Surface for this face -- if valid (skipping
// holes and boundary faces in some rare cases):
//
if (!meshSurfaceFactory.InitVertexSurface(faceIndex, &faceSurface)) {
continue;
}
//
// Get the Parameterization of the Surface and use it to identify
// coordinates for evaluation -- in this case, at the vertices
// and center of the face to create a fan of triangles:
//
Bfr::Parameterization faceParam = faceSurface.GetParameterization();
int faceSize = faceParam.GetFaceSize();
int numOutCoords = faceSize + 1;
outCoords.resize(numOutCoords * 2);
for (int i = 0; i < faceSize; ++i) {
faceParam.GetVertexCoord(i, &outCoords[i*2]);
}
faceParam.GetCenterCoord(&outCoords[faceSize*2]);
//
// Prepare the patch points for the Surface, then use them to
// evaluate output points for all identified coordinates:
//
// Resize patch point and output arrays:
int pointSize = 3;
facePatchPoints.resize(faceSurface.GetNumPatchPoints() * pointSize);
outPos.resize(numOutCoords * pointSize);
outDu.resize(numOutCoords * pointSize);
outDv.resize(numOutCoords * pointSize);
// Populate patch point and output arrays:
faceSurface.PreparePatchPoints(meshVertexPositions.data(), pointSize,
facePatchPoints.data(), 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]);
}
//
// Identify the faces of the tessellation, i.e. the triangle fan
// connecting points at the vertices to the center (last) point:
//
// Note the need to offset vertex indices for the output faces --
// using the number of vertices generated prior to this face.
//
int objVertexIndexOffset = objWriter.GetNumVertices();
outTriangles.resize(faceSize * 3);
int * outTriangle = outTriangles.data();
for (int i = 0; i < faceSize; ++i, outTriangle += 3) {
outTriangle[0] = objVertexIndexOffset + i;
outTriangle[1] = objVertexIndexOffset + (i + 1) % faceSize;
outTriangle[2] = objVertexIndexOffset + faceSize;
}
//
// Write the evaluated points and faces connecting them as Obj:
//
objWriter.WriteGroupName("baseFace_", faceIndex);
objWriter.WriteVertexPositions(outPos);
objWriter.WriteVertexNormals(outDu, outDv);
objWriter.WriteFaces(outTriangles, 3, true, false);
}
}
//
// Load command line arguments, specified or default geometry and process:
//
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;
}
//------------------------------------------------------------------------------
Generated on: 2025-08-28 03:27 UTC.
