far_tutorial_1.cpp

far_tutorial_1.cpp


https://github.com/PixarAnimationStudios/OpenSubdiv/blob/master/tutorials/far/tutorial_1/far_tutorial_1.cpp


//------------------------------------------------------------------------------
// 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) {

    typedef Far::IndexArray      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");
        }
    }
}

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