# far_tutorial_3_1.cpp

https://github.com/PixarAnimationStudios/OpenSubdiv/blob/release/tutorials/far/tutorial_3_1/far_tutorial_3_1.cpp

System Message: WARNING/2 (`/build/opensubdiv/src/OpenSubdiv-3_6_1/build/documentation/far_tutorial_3_1.rst`, line 9)

Cannot analyze code. Pygments package not found.

    .. code:: c++

        //------------------------------------------------------------------------------
        // Tutorial description:
        //
        // This tutorial shows how to interface a high-level topology representation
        // with Far for better efficiency. In tutorial 0, we showed how to instantiate
        // topology from a simple face-vertex list. Here we will show how to take
        // advantage of more complex data structures.
        //
        // Many client applications that manipulate geometry use advanced data structures
        // such as half-edge, quad-edge or winged-edge in order to represent complex
        // topological relationships beyond the usual face-vertex lists. We can take
        // advantage of this information.
        //
        // Far provides an advanced interface that allows such a client application to
        // communicate advanced component relationships directly and avoid having Far
        // rebuilding them redundantly.
        //

        #include <opensubdiv/far/topologyRefinerFactory.h>
        #include <opensubdiv/far/primvarRefiner.h>

        #include <cstdio>

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

        using namespace OpenSubdiv;

        //------------------------------------------------------------------------------
        //
        // For this tutorial, we provide the complete topological representation of a
        // simple pyramid. In our case, we store it as a simple sequence of integers,
        // with the understanding that client-code would provide a fully implemented
        // data-structure such as quad-edges or winged-edges.
        //
        // Pyramid geometry from catmark_pyramid.h - extended for this tutorial
        //
        static int g_nverts = 5,
                   g_nedges = 8,
                   g_nfaces = 5;

        // vertex positions
        static float g_verts[5][3] = {{ 0.0f,  0.0f,  2.0f},
                                      { 0.0f, -2.0f,  0.0f},
                                      { 2.0f,  0.0f,  0.0f},
                                      { 0.0f,  2.0f,  0.0f},
                                      {-2.0f,  0.0f,  0.0f}};

        // number of vertices in each face
        static int g_facenverts[5] = { 3, 3, 3, 3, 4 };

        // index of face vertices
        static int g_faceverts[16] = { 0, 1, 2,
                                       0, 2, 3,
                                       0, 3, 4,
                                       0, 4, 1,
                                       4, 3, 2, 1 };

        // index of edge vertices (2 per edge)
        static int g_edgeverts[16] = { 0, 1,
                                       1, 2,
                                       2, 0,
                                       2, 3,
                                       3, 0,
                                       3, 4,
                                       4, 0,
                                       4, 1 };

        // index of face edges
        static int g_faceedges[16] = { 0, 1, 2,
                                       2, 3, 4,
                                       4, 5, 6,
                                       6, 7, 0,
                                       5, 3, 1, 7 };

        // number of faces adjacent to each edge
        static int g_edgenfaces[8] = { 2, 2, 2, 2, 2, 2, 2, 2 };

        // index of faces incident to a given edge
        static int g_edgefaces[16] = { 0, 3,
                                       0, 4,
                                       0, 1,
                                       1, 4,
                                       1, 2,
                                       2, 4,
                                       2, 3,
                                       3, 4 };

        // number of faces incident to each vertex
        static int g_vertexnfaces[5] = { 4, 3, 3, 3, 3 };

        // index of faces incident to each vertex
        static int g_vertexfaces[25] = { 0, 1, 2, 3,
                                         0, 3, 4,
                                         0, 4, 1,
                                         1, 4, 2,
                                         2, 4, 3 };

        // number of edges incident to each vertex
        static int g_vertexnedges[5] = { 4, 3, 3, 3, 3 };

        // index of edges incident to each vertex
        static int g_vertexedges[25] = { 0, 2, 4, 6,
                                         1, 0, 7,
                                         2, 1, 3,
                                         4, 3, 5,
                                         6, 5, 7 };

        // Edge crease sharpness
        static float g_edgeCreases[8] = { 0.0f,
                                          2.5f,
                                          0.0f,
                                          2.5f,
                                          0.0f,
                                          2.5f,
                                          0.0f,
                                          2.5f };

        //------------------------------------------------------------------------------
        //
        // Because existing client-code may not provide an exact match for the
        // topological queries required by Far's interface, we can provide a converter
        // class. This can be particularly useful for instance if the client
        // data-structure requires additional relationships to be mapped. For instance,
        // half-edge representations do not store unique edge indices and it can be
        // difficult to traverse edges or faces adjacent to a given vertex.
        //
        // Using an intermediate wrapper class allows us to leverage existing
        // relationships information from a mesh, and generate the missing components
        // temporarily.
        //
        // For a practical example, you can look at the file 'hbr_to_vtr.h' in the same
        // tutorial directory. This example implements a 'OsdHbrConverter' class as a
        // way of interfacing PRman's half-edge representation to Far.
        //
        struct Converter {

        public:

            Sdc::SchemeType GetType() const {
                return Sdc::SCHEME_CATMARK;
            }

            Sdc::Options GetOptions() const {
                Sdc::Options options;
                options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);
                return options;
            }

            int GetNumFaces() const { return g_nfaces; }

            int GetNumEdges() const { return g_nedges; }

            int GetNumVertices() const { return g_nverts; }

            //
            // Face relationships
            //
            int GetNumFaceVerts(int face) const { return g_facenverts[face]; }

            int const * GetFaceVerts(int face) const { return g_faceverts+getCompOffset(g_facenverts, face); }

            int const * GetFaceEdges(int face) const { return g_faceedges+getCompOffset(g_facenverts, face); }

            //
            // Edge relationships
            //
            int const * GetEdgeVertices(int edge) const { return g_edgeverts+edge*2; }

            int GetNumEdgeFaces(int edge) const { return g_edgenfaces[edge]; }

            int const * GetEdgeFaces(int edge) const { return g_edgefaces+getCompOffset(g_edgenfaces, edge); }

            //
            // Vertex relationships
            //
            int GetNumVertexEdges(int vert) const { return g_vertexnedges[vert]; }

            int const * GetVertexEdges(int vert) const { return g_vertexedges+getCompOffset(g_vertexnedges, vert); }

            int GetNumVertexFaces(int vert) const { return g_vertexnfaces[vert]; }

            int const * GetVertexFaces(int vert) const { return g_vertexfaces+getCompOffset(g_vertexnfaces, vert); }

        private:

            int getCompOffset(int const * comps, int comp) const {
                int ofs=0;
                for (int i=0; i<comp; ++i) {
                    ofs += comps[i];
                }
                return ofs;
            }

        };

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

        namespace OpenSubdiv {
        namespace OPENSUBDIV_VERSION {

        namespace Far {

        template <>
        bool
        TopologyRefinerFactory<Converter>::resizeComponentTopology(
            TopologyRefiner & refiner, Converter const & conv) {

            // Faces and face-verts
            int nfaces = conv.GetNumFaces();
            setNumBaseFaces(refiner, nfaces);
            for (int face=0; face<nfaces; ++face) {

                int nv = conv.GetNumFaceVerts(face);
                setNumBaseFaceVertices(refiner, face, nv);
            }

           // Edges and edge-faces
            int nedges = conv.GetNumEdges();
            setNumBaseEdges(refiner, nedges);
            for (int edge=0; edge<nedges; ++edge) {

                int nf = conv.GetNumEdgeFaces(edge);
                setNumBaseEdgeFaces(refiner, edge, nf);
            }

            // Vertices and vert-faces and vert-edges
            int nverts = conv.GetNumVertices();
            setNumBaseVertices(refiner, nverts);
            for (int vert=0; vert<nverts; ++vert) {

                int ne = conv.GetNumVertexEdges(vert),
                    nf = conv.GetNumVertexFaces(vert);
                setNumBaseVertexEdges(refiner, vert, ne);
                setNumBaseVertexFaces(refiner, vert, nf);
            }
            return true;
        }

        template <>
        bool
        TopologyRefinerFactory<Converter>::assignComponentTopology(
            TopologyRefiner & refiner, Converter const & conv) {

            using Far::IndexArray;

            { // Face relations:
                int nfaces = conv.GetNumFaces();
                for (int face=0; face<nfaces; ++face) {

                    IndexArray dstFaceVerts = getBaseFaceVertices(refiner, face);
                    IndexArray dstFaceEdges = getBaseFaceEdges(refiner, face);

                    int const * faceverts = conv.GetFaceVerts(face);
                    int const * faceedges = conv.GetFaceEdges(face);

                    for (int vert=0; vert<conv.GetNumFaceVerts(face); ++vert) {
                        dstFaceVerts[vert] = faceverts[vert];
                        dstFaceEdges[vert] = faceedges[vert];
                    }
                }
            }

            { // Edge relations
              //
              // Note: if your representation is unable to provide edge relationships
              //       (ex: half-edges), you can comment out this section and Far will
              //       automatically generate the missing information.
              //
                int nedges = conv.GetNumEdges();
                for (int edge=0; edge<nedges; ++edge) {

                    //  Edge-vertices:
                    IndexArray dstEdgeVerts = getBaseEdgeVertices(refiner, edge);
                    dstEdgeVerts[0] = conv.GetEdgeVertices(edge)[0];
                    dstEdgeVerts[1] = conv.GetEdgeVertices(edge)[1];

                    //  Edge-faces
                    IndexArray dstEdgeFaces = getBaseEdgeFaces(refiner, edge);
                    for (int face=0; face<conv.GetNumEdgeFaces(face); ++face) {
                        dstEdgeFaces[face] = conv.GetEdgeFaces(edge)[face];
                    }
                }
            }

            { // Vertex relations
                int nverts = conv.GetNumVertices();
                for (int vert=0; vert<nverts; ++vert) {

                    //  Vert-Faces:
                    IndexArray vertFaces = getBaseVertexFaces(refiner, vert);
                    //LocalIndexArray vertInFaceIndices = getBaseVertexFaceLocalIndices(refiner, vert);
                    for (int face=0; face<conv.GetNumVertexFaces(vert); ++face) {
                        vertFaces[face] = conv.GetVertexFaces(vert)[face];
                    }

                    //  Vert-Edges:
                    IndexArray vertEdges = getBaseVertexEdges(refiner, vert);
                    //LocalIndexArray vertInEdgeIndices = getBaseVertexEdgeLocalIndices(refiner, vert);
                    for (int edge=0; edge<conv.GetNumVertexEdges(vert); ++edge) {
                        vertEdges[edge] = conv.GetVertexEdges(vert)[edge];
                    }
                }
            }

            populateBaseLocalIndices(refiner);

            return true;
        };

        template <>
        bool
        TopologyRefinerFactory<Converter>::assignComponentTags(
            TopologyRefiner & refiner, Converter const & conv) {

            // arbitrarily sharpen the 4 bottom edges of the pyramid to 2.5f
            for (int edge=0; edge<conv.GetNumEdges(); ++edge) {
                setBaseEdgeSharpness(refiner, edge, g_edgeCreases[edge]);
            }
            return true;
        }

        #ifdef _MSC_VER
        template <>
        void
        TopologyRefinerFactory<Converter>::reportInvalidTopology(
            TopologyError /* errCode */, char const * msg, Converter const& /* mesh */) {

            //
            //  Optional topology validation error reporting:
            //      This method is called whenever the factory encounters topology validation
            //  errors. By default, nothing is reported
            //
            Warning(msg);
        }
        template <>
        bool
        TopologyRefinerFactory<Converter>::assignFaceVaryingTopology(
            TopologyRefiner & /* refiner */, Converter const & /* conv */) {

            // Because of the way MSVC++ specializes templated functions, we had to
            // remove the default stubs in Far::TopologyRefinerFactory. In this
            // example, no face-varying data is being added, but we still need to
            // implement a template specialization or MSVC++ linker fails.
            return true;
        }
        #endif

        } // namespace Far

        } // namespace OPENSUBDIV_VERSION
        } // namespace OpenSubdiv

        //------------------------------------------------------------------------------
        //
        // Vertex container implementation.
        //
        struct Vertex {

            // Minimal required interface ----------------------
            Vertex() { }

            Vertex(Vertex const & src) {
                _position[0] = src._position[0];
                _position[1] = src._position[1];
                _position[2] = src._position[2];
            }

            void Clear( void * =0 ) {
                _position[0]=_position[1]=_position[2]=0.0f;
            }

            void AddWithWeight(Vertex const & src, float weight) {
                _position[0]+=weight*src._position[0];
                _position[1]+=weight*src._position[1];
                _position[2]+=weight*src._position[2];
            }

            // Public interface ------------------------------------
            void SetPosition(float x, float y, float z) {
                _position[0]=x;
                _position[1]=y;
                _position[2]=z;
            }

            const float * GetPosition() const {
                return _position;
            }

        private:
            float _position[3];
        };

        //------------------------------------------------------------------------------
        int main(int, char **) {

            Converter conv;

            Far::TopologyRefiner * refiner =
                Far::TopologyRefinerFactory<Converter>::Create(conv,
                        Far::TopologyRefinerFactory<Converter>::Options(conv.GetType(), conv.GetOptions()));

            int maxlevel = 5;

            // Uniformly refine the topology up to 'maxlevel'
            refiner->RefineUniform(Far::TopologyRefiner::UniformOptions(maxlevel));

            // Allocate a buffer for vertex primvar data. The buffer length is set to
            // be the sum of all children vertices up to the highest level of refinement.
            std::vector<Vertex> vbuffer(refiner->GetNumVerticesTotal());
            Vertex * verts = &vbuffer[0];

            // Initialize coarse mesh positions
            int nCoarseVerts = g_nverts;
            for (int i=0; i<nCoarseVerts; ++i) {
                verts[i].SetPosition(g_verts[i][0], g_verts[i][1], g_verts[i][2]);
            }

            // Interpolate vertex primvar data
            Far::PrimvarRefiner primvarRefiner(*refiner);

            Vertex * src = verts;
            for (int level = 1; level <= maxlevel; ++level) {
                Vertex * dst = src + refiner->GetLevel(level-1).GetNumVertices();
                primvarRefiner.Interpolate(level, src, dst);
                src = dst;
            }

            { // Output OBJ of the highest level refined -----------

                Far::TopologyLevel const & refLastLevel = refiner->GetLevel(maxlevel);

                int nverts = refLastLevel.GetNumVertices();
                int nfaces = refLastLevel.GetNumFaces();

                // Print vertex positions
                int firstOfLastVerts = refiner->GetNumVerticesTotal() - nverts;

                for (int vert = 0; vert < nverts; ++vert) {
                    float const * pos = verts[firstOfLastVerts + vert].GetPosition();
                    printf("v %f %f %f\n", pos[0], pos[1], pos[2]);
                }

                // Print faces
                for (int face = 0; face < nfaces; ++face) {

                    Far::ConstIndexArray fverts = refLastLevel.GetFaceVertices(face);

                    // all refined Catmark faces should be quads
                    assert(fverts.size()==4);

                    printf("f ");
                    for (int vert=0; vert<fverts.size(); ++vert) {
                        printf("%d ", fverts[vert]+1); // OBJ uses 1-based arrays...
                    }
                    printf("\n");
                }
            }

            delete refiner;
            return EXIT_SUCCESS;
        }

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

- User Docs
- API Docs
- Release Notes
- Forum

TM & © 1986 - 2017 PIXAR. ALL RIGHTS RESERVED. Terms of Use