static void
createMesh(ShapeDesc const & shapeDesc, int level) {

    typedef Far::LimitStencilTableFactory::LocationArray LocationArray;

    Shape const * shape = Shape::parseObj(shapeDesc.data.c_str(), shapeDesc.scheme);

    // create Far mesh (topology)
    OpenSubdiv::Sdc::SchemeType sdctype = GetSdcType(*shape);
    OpenSubdiv::Sdc::Options sdcoptions = GetSdcOptions(*shape);

    OpenSubdiv::Far::TopologyRefiner * refiner =
        OpenSubdiv::Far::TopologyRefinerFactory<Shape>::Create(*shape,
            OpenSubdiv::Far::TopologyRefinerFactory<Shape>::Options(sdctype, sdcoptions));

    // save coarse topology (used for coarse mesh drawing)
    OpenSubdiv::Far::TopologyLevel const & refBaseLevel = refiner->GetLevel(0);

    g_controlMeshDisplay.SetTopology(refBaseLevel);
    int nverts = refBaseLevel.GetNumVertices();

    // save rest pose
    g_orgPositions = shape->verts;

    if (g_bilinear) {
        Far::TopologyRefiner::UniformOptions options(level);
        options.fullTopologyInLastLevel = true;
        refiner->RefineUniform(options);
    } else {
        Far::TopologyRefiner::AdaptiveOptions options(level);
        options.useSingleCreasePatch = false;
        refiner->RefineAdaptive(options);
    }

    Far::PtexIndices ptexIndices(*refiner);
    int nfaces = ptexIndices.GetNumFaces();

    float * u = new float[g_nsamples*nfaces], * uPtr = u,
          * v = new float[g_nsamples*nfaces], * vPtr = v;

    std::vector<LocationArray> locs(nfaces);

    srand( static_cast<int>(2147483647) ); // use a large Pell prime number
    for (int face=0; face<nfaces; ++face) {

        LocationArray & larray = locs[face];
        larray.ptexIdx = face;
        larray.numLocations = g_nsamples;
        larray.s = uPtr;
        larray.t = vPtr;

        for (int j=0; j<g_nsamples; ++j, ++uPtr, ++vPtr) {
            *uPtr = (float)rand()/(float)RAND_MAX;
            *vPtr = (float)rand()/(float)RAND_MAX;
        }
    }

    delete g_controlStencils;
    g_controlStencils = Far::LimitStencilTableFactory::Create(*refiner, locs);

    delete [] u;
    delete [] v;

    g_nsamplesDrawn = g_controlStencils->GetNumStencils();

    delete shape;
    delete refiner;

    delete g_stencilOutput;
    if (g_kernel == kCPU) {
        g_stencilOutput = new StencilOutput<Osd::CpuGLVertexBuffer,
                                            Osd::CpuGLVertexBuffer,
                                            Far::LimitStencilTable,
                                            Osd::CpuEvaluator>(
                                                g_controlStencils, nverts);
#ifdef OPENSUBDIV_HAS_OPENMP
    } else if (g_kernel == kOPENMP) {
        g_stencilOutput = new StencilOutput<Osd::CpuGLVertexBuffer,
                                            Osd::CpuGLVertexBuffer,
                                            Far::LimitStencilTable,
                                            Osd::OmpEvaluator>(
                                                g_controlStencils, nverts);
#endif
#ifdef OPENSUBDIV_HAS_TBB
    } else if (g_kernel == kTBB) {
        g_stencilOutput = new StencilOutput<Osd::CpuGLVertexBuffer,
                                            Osd::CpuGLVertexBuffer,
                                            Far::LimitStencilTable,
                                            Osd::TbbEvaluator>(
                                                g_controlStencils, nverts);
#endif
#ifdef OPENSUBDIV_HAS_CUDA
    } else if (g_kernel == kCUDA) {
        g_stencilOutput = new StencilOutput<Osd::CudaGLVertexBuffer,
                                            Osd::CudaGLVertexBuffer,
                                            Osd::CudaStencilTable,
                                            Osd::CudaEvaluator>(
                                                g_controlStencils, nverts);
#endif
#ifdef OPENSUBDIV_HAS_OPENCL
    } else if (g_kernel == kCL) {
        static Osd::EvaluatorCacheT<Osd::CLEvaluator> clEvaluatorCache;
        g_stencilOutput = new StencilOutput<Osd::CLGLVertexBuffer,
                                            Osd::CLGLVertexBuffer,
                                            Osd::CLStencilTable,
                                            Osd::CLEvaluator,
                                            CLDeviceContext>(
                                                g_controlStencils, nverts,
                                                &clEvaluatorCache,
                                                &g_clDeviceContext);
#endif
#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
    } else if (g_kernel == kGLXFB) {
        static Osd::EvaluatorCacheT<Osd::GLXFBEvaluator> glXFBEvaluatorCache;
        g_stencilOutput = new StencilOutput<Osd::GLVertexBuffer,
                                            Osd::GLVertexBuffer,
                                            Osd::GLStencilTableTBO,
                                            Osd::GLXFBEvaluator>(
                                                g_controlStencils, nverts,
                                                 &glXFBEvaluatorCache);
#endif
#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
    } else if (g_kernel == kGLCompute) {
        static Osd::EvaluatorCacheT<Osd::GLComputeEvaluator> glComptueEvaluatorCache;
        g_stencilOutput = new StencilOutput<Osd::GLVertexBuffer,
                                            Osd::GLVertexBuffer,
                                            Osd::GLStencilTableSSBO,
                                            Osd::GLComputeEvaluator>(
                                                g_controlStencils, nverts,
                                                    &glComptueEvaluatorCache);
#endif
    }

    updateGeom();
}
Exemple #2
0
MStatus
MayaPolySmooth::compute( const MPlug& plug, MDataBlock& data ) {

    MStatus status;

    // Check which output attribute we have been asked to compute.  If this
    // node doesn't know how to compute it, we must return
    // MS::kUnknownParameter.
    //

    if( plug == a_output ) {

        bool createdSubdMesh = false;

        int subdivisionLevel = data.inputValue(a_subdivisionLevels).asInt();
        short stateH = data.inputValue(state).asShort();

        if ((subdivisionLevel > 0) and (stateH !=1)) {

            // == Retrieve input mesh ====================================
            // Get attr values
            MObject inMeshObj        = data.inputValue(a_inputPolymesh).asMesh();
            short vertBoundaryMethod = data.inputValue(a_vertBoundaryMethod).asShort();
            short fvarBoundaryMethod = data.inputValue(a_fvarBoundaryMethod).asShort();
            bool  fvarPropCorners    = data.inputValue(a_fvarPropagateCorners).asBool();
            bool  smoothTriangles    = data.inputValue(a_smoothTriangles).asBool();
            short creaseMethodVal    = data.inputValue(a_creaseMethod).asShort();

            // == Get Mesh Functions and Iterators ==========================
            MFnMeshData inMeshDat(inMeshObj);
            MFnMesh inMeshFn(inMeshObj, &status);
            MCHECKERR(status, "ERROR getting inMeshFn\n");
            MItMeshPolygon inMeshItPolygon(inMeshObj, &status);
            MCHECKERR(status, "ERROR getting inMeshItPolygon\n");

            // Convert attr values to OSD enums
            OpenSubdiv::Sdc::SchemeType type = OpenSubdiv::Sdc::SCHEME_CATMARK;

            // == Create Far topology ==========================
            OpenSubdiv::Sdc::Options options;
            options.SetVtxBoundaryInterpolation(ConvertMayaVtxBoundary(vertBoundaryMethod));
            options.SetFVarLinearInterpolation(ConvertMayaFVarBoundary(fvarBoundaryMethod, fvarPropCorners));
            options.SetCreasingMethod(creaseMethodVal ?
                 OpenSubdiv::Sdc::Options::CREASE_CHAIKIN : OpenSubdiv::Sdc::Options::CREASE_UNIFORM);
            options.SetTriangleSubdivision(smoothTriangles ?
                 OpenSubdiv::Sdc::Options::TRI_SUB_SMOOTH : OpenSubdiv::Sdc::Options::TRI_SUB_CATMARK);

            // Storage for face-varying values (UV sets, vertex colors...)
            std::vector<MFloatArray> uvSet_uCoords;
            std::vector<MFloatArray> uvSet_vCoords;
            std::vector<MColorArray> colorSet_colors;

            bool hasUVs = false, hasColors = false;
            float maxCreaseSharpness=0.0f;
            OpenSubdiv::Far::TopologyRefiner * refiner = gatherTopology(
                inMeshFn, inMeshItPolygon, type, options, &hasUVs, &hasColors,
                uvSet_uCoords, uvSet_vCoords, colorSet_colors, &maxCreaseSharpness);

            assert(refiner);

            // == Refine & Interpolate ==========================
            refiner->RefineUniform(OpenSubdiv::Far::TopologyRefiner::UniformOptions(subdivisionLevel));

            // Prepare vertex information
            Vertex const * initialVerts = 
                reinterpret_cast<Vertex const *>(inMeshFn.getRawPoints(&status));
            std::vector<Vertex> refinedVerts(
                refiner->GetNumVerticesTotal() - refiner->GetLevel(0).GetNumVertices());
            Vertex const * srcVerts = &initialVerts[0];
            Vertex * dstVerts = &refinedVerts[0];
           
            // Verify the refiner has the correct number of values 
            // needed to interpolate the different channels
            int numInitialUVs = refiner->GetLevel(0).GetNumFVarValues(CHANNELUV);
            int numInitialColors = refiner->GetLevel(0).GetNumFVarValues(CHANNELCOLOR);

            if (hasUVs && numInitialUVs <= 0) {
                hasUVs = false;
                MGlobal::displayError("Model with incorrect data, the UV channel will not be interpolated.");
            }

            if (hasColors && numInitialColors <= 0) {
                hasColors = false;  
                MGlobal::displayError("Model with incorrect data, the color channel will not be interpolated.");
            } 

            // Prepare UV information if needed
            std::vector<FVarVertexUV> initialUVs, refinedUVs;
            FVarVertexUV const * srcUV = NULL;
            FVarVertexUV * dstUV = NULL;
            if(hasUVs) {
                initialUVs.resize(numInitialUVs);
                refinedUVs.resize(refiner->GetNumFVarValuesTotal(CHANNELUV));
                for (int i=0; i<numInitialUVs; ++i) {
                    initialUVs[i].u = uvSet_uCoords[0][i];
                    initialUVs[i].v = uvSet_vCoords[0][i];
                }
                srcUV = &initialUVs[0];
                dstUV = &refinedUVs[0];
            }

            // Prepare color information if needed
            std::vector<FVarVertexColor> initialColors, refinedColors;
            FVarVertexColor const * srcColor = NULL;
            FVarVertexColor * dstColor = NULL;
            if(hasColors) {
                initialColors.resize(numInitialColors);
                refinedColors.resize(refiner->GetNumFVarValuesTotal(CHANNELCOLOR));
                for (int i=0; i<numInitialColors; ++i) {
                    initialColors[i].r = colorSet_colors[0][i].r;
                    initialColors[i].g = colorSet_colors[0][i].g;
                    initialColors[i].b = colorSet_colors[0][i].b;
                    initialColors[i].a = colorSet_colors[0][i].a;
                }
                srcColor = &initialColors[0];
                dstColor = &refinedColors[0];
            }

            // Interpolate the vertices and the different channels
            OpenSubdiv::Far::PrimvarRefiner primvarRefiner(*refiner); 
            
            for (int level = 1; level <= subdivisionLevel; ++level) {
                
                // Interpolate vertices
                primvarRefiner.Interpolate(level, srcVerts, dstVerts);
                srcVerts = dstVerts;
                dstVerts += refiner->GetLevel(level).GetNumVertices();

                // Interpolate the uv set
                if(hasUVs) {
                    primvarRefiner.InterpolateFaceVarying(level, srcUV, dstUV, CHANNELUV);
                    srcUV = dstUV;
                    dstUV += refiner->GetLevel(level).GetNumFVarValues(CHANNELUV);
                }

                // Interpolate any color set
                if(hasColors) {
                    primvarRefiner.InterpolateFaceVarying(level, srcColor, dstColor, CHANNELCOLOR);
                    srcColor = dstColor;
                    dstColor += refiner->GetLevel(level).GetNumFVarValues(CHANNELCOLOR);
                }
            }

            // == Convert subdivided OpenSubdiv mesh to MFnMesh Data outputMesh =============

            // Create New Mesh Data Object
            MFnMeshData newMeshData;
            MObject     newMeshDataObj = newMeshData.create(&status);
            MCHECKERR(status, "ERROR creating outputData");

            // Create out mesh
            status = convertToMayaMeshData(*refiner, refinedVerts, hasUVs, 
                refinedUVs, hasColors, refinedColors, inMeshFn, newMeshDataObj);
            MCHECKERR(status, "ERROR convertOsdFarToMayaMesh");

            // Propagate objectGroups from inMesh to outMesh (for per-facet shading, etc)
            status = createSmoothMesh_objectGroups(inMeshFn, inMeshDat,
                newMeshData, subdivisionLevel, refiner->GetLevel(subdivisionLevel).GetNumFaces());

            // Write to output plug
            MDataHandle outMeshH = data.outputValue(a_output, &status);
            MCHECKERR(status, "ERROR getting polygon data handle\n");
            outMeshH.set(newMeshDataObj);

            int isolation = std::min(10,(int)ceil(maxCreaseSharpness)+1);
            data.outputValue(a_recommendedIsolation).set(isolation);

            // == Cleanup OSD ============================================

            // REVISIT: Re-add these deletes
            delete refiner;

            // note that the subd mesh was created (see the section below if !createdSubdMesh)
            createdSubdMesh = true;
        }

        // Pass-through inMesh to outMesh if not created the subd mesh
        if (!createdSubdMesh) {
            MDataHandle outMeshH = data.outputValue(a_output, &status);
            status = outMeshH.copy(data.outputValue(a_inputPolymesh, &status));
            MCHECKERR(status, "ERROR getting polygon data handle\n");
        }

        // Clean up Maya Plugs
        data.setClean(plug);

    } else {
        // Unhandled parameter in this compute function, so return MS::kUnknownParameter
        // so it is handled in a parent compute() function.
        return MS::kUnknownParameter;
    }
    return MS::kSuccess;
}
static void
createMesh(ShapeDesc const & shapeDesc, int level) {

    typedef Far::ConstIndexArray IndexArray;
    typedef Far::LimitStencilTablesFactory::LocationArray LocationArray;

    Shape const * shape = Shape::parseObj(shapeDesc.data.c_str(), shapeDesc.scheme);

    // create Vtr mesh (topology)
    OpenSubdiv::Sdc::SchemeType sdctype = GetSdcType(*shape);
    OpenSubdiv::Sdc::Options sdcoptions = GetSdcOptions(*shape);

    OpenSubdiv::Far::TopologyRefiner * refiner =
        OpenSubdiv::Far::TopologyRefinerFactory<Shape>::Create(*shape,
            OpenSubdiv::Far::TopologyRefinerFactory<Shape>::Options(sdctype, sdcoptions));

    // save coarse topology (used for coarse mesh drawing)
    int nedges = refiner->GetNumEdges(0),
        nverts = refiner->GetNumVertices(0);

    g_coarseEdges.resize(nedges*2);
    g_coarseEdgeSharpness.resize(nedges);
    g_coarseVertexSharpness.resize(nverts);

    for(int i=0; i<nedges; ++i) {
        IndexArray verts = refiner->GetEdgeVertices(0, i);
        g_coarseEdges[i*2  ]=verts[0];
        g_coarseEdges[i*2+1]=verts[1];
        g_coarseEdgeSharpness[i]=refiner->GetEdgeSharpness(0, i);
    }

    for(int i=0; i<nverts; ++i) {
        g_coarseVertexSharpness[i]=refiner->GetVertexSharpness(0, i);
    }

    g_orgPositions=shape->verts;

    if (g_bilinear) {
        Far::TopologyRefiner::UniformOptions options(level);
        options.fullTopologyInLastLevel = true;
        refiner->RefineUniform(options);
    } else {
        Far::TopologyRefiner::AdaptiveOptions options(level);
        options.fullTopologyInLastLevel = false;
        options.useSingleCreasePatch = false;
        refiner->RefineAdaptive(options);
    }

    int nfaces = refiner->GetNumPtexFaces();

    float * u = new float[g_nsamples*nfaces], * uPtr = u,
          * v = new float[g_nsamples*nfaces], * vPtr = v;

    std::vector<LocationArray> locs(nfaces);

    srand( static_cast<int>(2147483647) ); // use a large Pell prime number
    for (int face=0; face<nfaces; ++face) {

        LocationArray & larray = locs[face];
        larray.ptexIdx = face;
        larray.numLocations = g_nsamples;
        larray.s = uPtr;
        larray.t = vPtr;

        for (int j=0; j<g_nsamples; ++j, ++uPtr, ++vPtr) {
            *uPtr = (float)rand()/(float)RAND_MAX;
            *vPtr = (float)rand()/(float)RAND_MAX;
        }
    }

    delete g_controlStencils;
    g_controlStencils = Far::LimitStencilTablesFactory::Create(*refiner, locs);

    delete [] u;
    delete [] v;

    g_nsamplesDrawn = g_controlStencils->GetNumStencils();

    // Create control vertex buffer (layout: [ P(xyz) ] )
    delete g_controlValues;
    g_controlValues = Osd::CpuVertexBuffer::Create(3, nverts);

    // Create eval context & data buffers
    delete g_evalCtx;
    g_evalCtx = Osd::CpuEvalStencilsContext::Create(g_controlStencils);

    delete g_stencilValues;
    g_stencilValues = Osd::CpuGLVertexBuffer::Create(3, g_controlStencils->GetNumStencils() * 6 );

    delete shape;
    delete refiner;

    updateGeom();

    // Bind g_stencilValues as GL_LINES VAO
    glBindVertexArray(g_stencilsVAO);

    glBindBuffer(GL_ARRAY_BUFFER, g_stencilValues->BindVBO());

    glBindVertexArray(0);

}
//------------------------------------------------------------------------------
static void
createVtrMesh(Shape * shape, int maxlevel) {

    Stopwatch s;
    s.Start();

    // create Vtr mesh (topology)
    OpenSubdiv::Sdc::Type       sdctype = GetSdcType(*shape);
    OpenSubdiv::Sdc::Options sdcoptions = GetSdcOptions(*shape);

    OpenSubdiv::Far::TopologyRefiner * refiner =
        OpenSubdiv::Far::TopologyRefinerFactory<Shape>::Create(sdctype, sdcoptions, *shape);

    OpenSubdiv::Far::PatchTables * patchTables = 0;

    if (g_Adaptive) {

        refiner->RefineAdaptive(maxlevel, /*fullTopology*/true);

        patchTables = OpenSubdiv::Far::PatchTablesFactory::Create(*refiner);

        g_numPatches = patchTables->GetNumPatches();
    } else {
        refiner->RefineUniform(maxlevel, /*fullTopology*/true);
    }
    s.Stop();

    // create vertex primvar data buffer
    std::vector<Vertex> vertexBuffer(refiner->GetNumVerticesTotal());
    Vertex * verts = &vertexBuffer[0];

    //printf("Vtr time: %f ms (topology)\n", float(s.GetElapsed())*1000.0f);

    // copy coarse vertices positions
    int ncoarseverts = shape->GetNumVertices();
    for (int i=0; i<ncoarseverts; ++i) {
        float * ptr = &shape->verts[i*3];
        verts[i].SetPosition(ptr[0], ptr[1], ptr[2]);
    }

//#define no_stencils
#ifdef no_stencils
    {
        s.Start();
        // populate buffer with Vtr interpolated vertex data
        refiner->Interpolate(verts, verts + ncoarseverts);
        s.Stop();
        //printf("          %f ms (interpolate)\n", float(s.GetElapsed())*1000.0f);
        //printf("          %f ms (total)\n", float(s.GetTotalElapsed())*1000.0f);
    }
#else
    {
        OpenSubdiv::Far::StencilTablesFactory::Options options;
        options.generateOffsets=true;
        options.generateAllLevels=true;
        options.sortBySize=false;

        OpenSubdiv::Far::StencilTables const * stencilTables =
            OpenSubdiv::Far::StencilTablesFactory::Create(*refiner, options);

        stencilTables->UpdateValues(verts, verts + ncoarseverts);
    }
#endif

    if (g_VtrDrawVertIDs) {
        createVertNumbers(*refiner, vertexBuffer);
    }

    if (g_VtrDrawFaceIDs) {
        createFaceNumbers(*refiner, vertexBuffer);
    }

    if (g_VtrDrawPtexIDs and patchTables) {
        createPtexNumbers(*patchTables, vertexBuffer);
    }

    if (g_Adaptive and patchTables) {
        createPatchNumbers(*patchTables, vertexBuffer);
    }

    createEdgeNumbers(*refiner, vertexBuffer, g_VtrDrawEdgeIDs!=0, g_VtrDrawEdgeSharpness!=0);

    GLMesh::Options options;
    options.vertColorMode=g_Adaptive ? GLMesh::VERTCOLOR_BY_LEVEL : GLMesh::VERTCOLOR_BY_SHARPNESS;
    options.edgeColorMode=g_Adaptive ? GLMesh::EDGECOLOR_BY_PATCHTYPE : GLMesh::EDGECOLOR_BY_SHARPNESS;
    options.faceColorMode=g_Adaptive ? GLMesh::FACECOLOR_BY_PATCHTYPE :GLMesh::FACECOLOR_SOLID;

    if (g_Adaptive) {
        g_vtr_glmesh.Initialize(options, *refiner, patchTables, (float *)&verts[0]);
        g_vtr_glmesh.SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
    } else {
        g_vtr_glmesh.Initialize(options, *refiner, patchTables, (float *)&verts[0]);
        g_vtr_glmesh.SetDiffuseColor(0.75f, 0.9f, 1.0f, 1.0f);
    }


    //setFaceColors(*refiner);

    g_vtr_glmesh.InitializeDeviceBuffers();

    delete refiner;
    delete patchTables;
}
MStatus
MayaPolySmooth::compute( const MPlug& plug, MDataBlock& data ) {

    MStatus status;

    // Check which output attribute we have been asked to compute.  If this
    // node doesn't know how to compute it, we must return
    // MS::kUnknownParameter.
    //

    if( plug == a_output ) {

        bool createdSubdMesh = false;

        int subdivisionLevel = data.inputValue(a_subdivisionLevels).asInt();
        short stateH = data.inputValue(state).asShort();

        if ((subdivisionLevel > 0) and (stateH !=1)) {

            // == Retrieve input mesh ====================================
            // Get attr values
            MObject inMeshObj        = data.inputValue(a_inputPolymesh).asMesh();
            short vertBoundaryMethod = data.inputValue(a_vertBoundaryMethod).asShort();
            short fvarBoundaryMethod = data.inputValue(a_fvarBoundaryMethod).asShort();
            bool  fvarPropCorners    = data.inputValue(a_fvarPropagateCorners).asBool();
            bool  smoothTriangles    = data.inputValue(a_smoothTriangles).asBool();
            short creaseMethodVal    = data.inputValue(a_creaseMethod).asShort();

            // == Get Mesh Functions and Iterators ==========================
            MFnMeshData inMeshDat(inMeshObj);
            MFnMesh inMeshFn(inMeshObj, &status);
            MCHECKERR(status, "ERROR getting inMeshFn\n");
            MItMeshPolygon inMeshItPolygon(inMeshObj, &status);
            MCHECKERR(status, "ERROR getting inMeshItPolygon\n");

            // Convert attr values to OSD enums
            OpenSubdiv::Sdc::SchemeType type = OpenSubdiv::Sdc::SCHEME_CATMARK;

            //
            // Create Far topology
            //
            OpenSubdiv::Sdc::Options options;
            options.SetVtxBoundaryInterpolation(ConvertMayaVtxBoundary(vertBoundaryMethod));
            options.SetFVarLinearInterpolation(ConvertMayaFVarBoundary(fvarBoundaryMethod, fvarPropCorners));
            options.SetCreasingMethod(creaseMethodVal ?
                 OpenSubdiv::Sdc::Options::CREASE_CHAIKIN : OpenSubdiv::Sdc::Options::CREASE_UNIFORM);
            options.SetTriangleSubdivision(smoothTriangles ?
                 OpenSubdiv::Sdc::Options::TRI_SUB_SMOOTH : OpenSubdiv::Sdc::Options::TRI_SUB_CATMARK);

            float maxCreaseSharpness=0.0f;
            OpenSubdiv::Far::TopologyRefiner * refiner =
                gatherTopology(inMeshFn, inMeshItPolygon, type, options, &maxCreaseSharpness);

            assert(refiner);

            // Refine & Interpolate
            refiner->RefineUniform(OpenSubdiv::Far::TopologyRefiner::UniformOptions(subdivisionLevel));

            Vertex const * controlVerts =
                reinterpret_cast<Vertex const *>(inMeshFn.getRawPoints(&status));

            std::vector<Vertex> refinedVerts(
                refiner->GetNumVerticesTotal() - refiner->GetLevel(0).GetNumVertices());
            
            Vertex const * srcVerts = controlVerts;
            Vertex * dstVerts = &refinedVerts[0];
            for (int level = 1; level <= subdivisionLevel; ++level) {
                OpenSubdiv::Far::PrimvarRefiner(*refiner).Interpolate(level, srcVerts, dstVerts);
                srcVerts = dstVerts;
                dstVerts += refiner->GetLevel(level).GetNumVertices();
            }

            // == Convert subdivided OpenSubdiv mesh to MFnMesh Data outputMesh =============

            // Create New Mesh Data Object
            MFnMeshData newMeshData;
            MObject     newMeshDataObj = newMeshData.create(&status);
            MCHECKERR(status, "ERROR creating outputData");

            // Create out mesh
            status = convertToMayaMeshData(*refiner, refinedVerts, inMeshFn, newMeshDataObj);
            MCHECKERR(status, "ERROR convertOsdFarToMayaMesh");

            // Propagate objectGroups from inMesh to outMesh (for per-facet shading, etc)
            status = createSmoothMesh_objectGroups(inMeshFn, inMeshDat,
                newMeshData, subdivisionLevel, refiner->GetLevel(subdivisionLevel).GetNumFaces());

            // Write to output plug
            MDataHandle outMeshH = data.outputValue(a_output, &status);
            MCHECKERR(status, "ERROR getting polygon data handle\n");
            outMeshH.set(newMeshDataObj);

            int isolation = std::min(10,(int)ceil(maxCreaseSharpness)+1);
            data.outputValue(a_recommendedIsolation).set(isolation);

            // == Cleanup OSD ============================================
            // REVISIT: Re-add these deletes
            delete refiner;

            // note that the subd mesh was created (see the section below if !createdSubdMesh)
            createdSubdMesh = true;
        }

        // Pass-through inMesh to outMesh if not created the subd mesh
        if (!createdSubdMesh) {
            MDataHandle outMeshH = data.outputValue(a_output, &status);
            status = outMeshH.copy(data.outputValue(a_inputPolymesh, &status));
            MCHECKERR(status, "ERROR getting polygon data handle\n");
        }

        // Clean up Maya Plugs
        data.setClean(plug);

    } else {
        // Unhandled parameter in this compute function, so return MS::kUnknownParameter
        // so it is handled in a parent compute() function.
        return MS::kUnknownParameter;
    }
    return MS::kSuccess;
}