# bfr_tutorial_1_1.cpp

https://github.com/PixarAnimationStudios/OpenSubdiv/blob/release/tutorials/bfr/tutorial_1_1/bfr_tutorial_1_1.cpp

    //------------------------------------------------------------------------------
    //  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;
    }

    //------------------------------------------------------------------------------