1

I am trying to save patches from OpenSubdiv subdivision using Open Cascade for one of my app that I am trying to build for subdivision surfaces. This is pretty similar to the thread - https://groups.google.com/g/opensubdiv/c/ujdolv75a4Q/m/djyq6xIqAAAJ

But I am not quite getting the result and I am not sure what I am missing. The overall shape of the output looks correct but surfaces are protruding into each other and they are not "smooth" at boundaries. So it looks like I am not able to query the patches right or something is wrong in converting the patches into the "Geom_BezierSurface" from Open Cascade. If you could just point me where I could start looking, that would be really helpful. I have spent some time on this but not getting anywhere.

Here is what my output looks like right now with 160 faces created.

enter image description here

If I just render first two faces from the output, they look like this

enter image description here

And here is my code

#include "Geom_BezierSurface.hxx"

#include <opensubdiv/far/topologyDescriptor.h>
#include <opensubdiv/far/primvarRefiner.h>
#include <opensubdiv/far/patchTableFactory.h>
#include <opensubdiv/far/patchMap.h>
#include <opensubdiv/far/ptexIndices.h>

#include <cassert>
#include <cstdio>
#include <cstring>
#include <cfloat>

using namespace OpenSubdiv;

typedef double Real;

static int const g_nverts = 5;
static double const g_verts[24] = { 0.0f,   0.0f, 20.0f,
                                    0.0f, -20.0f,  0.0f,
                                   20.0f,   0.0f,  0.0f,
                                    0.0f,  20.0f,  0.0f,
                                  -20.0f,   0.0f,  0.0f, };


static int const g_vertsperface[5] = { 3, 3, 3, 3, 4 };

static int const g_nfaces = 5;
static int const g_faceverts[16] = { 0, 1, 2,
                                     0, 2, 3,
                                     0, 3, 4,
                                     0, 4, 1,
                                     4, 3, 2, 1 };

static int const g_ncreases = 4;
static int const g_creaseverts[8] = { 4, 3, 3, 2, 2, 1, 1, 4 };
static float const g_creaseweights[4] = { 3.0f, 3.0f, 3.0f, 3.0f };

// Creates a Far::TopologyRefiner from the pyramid shape above
static Far::TopologyRefiner* createTopologyRefiner();

static Far::TopologyRefiner* createTopologyRefiner()
{


    typedef Far::TopologyDescriptor Descriptor;

    Sdc::SchemeType type = OpenSubdiv::Sdc::SCHEME_CATMARK;

    Sdc::Options options;
    options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);

    Descriptor desc;
    desc.numVertices = g_nverts;
    desc.numFaces = g_nfaces;
    desc.numVertsPerFace = g_vertsperface;
    desc.vertIndicesPerFace = g_faceverts;
    desc.numCreases = g_ncreases;
    desc.creaseVertexIndexPairs = g_creaseverts;
    desc.creaseWeights = g_creaseweights;

    // Instantiate a FarTopologyRefiner from the descriptor.
    Far::TopologyRefiner* refiner =
        Far::TopologyRefinerFactory<Descriptor>::Create(desc,
            Far::TopologyRefinerFactory<Descriptor>::Options(type, options));

    return refiner;
}

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

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

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

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

    Real point[3];
};

void CModelingDoc::OnFace() 
{
    // Generate a FarTopologyRefiner (see far_tutorial_0 for details).
    Far::TopologyRefiner* refiner = createTopologyRefiner();

    // Adaptively refine the topology with an isolation level capped at 3
    // because the sharpest crease in the shape is 3.0f (in g_creaseweights[])
    int maxIsolation = 3;
    refiner->RefineAdaptive(
        Far::TopologyRefiner::AdaptiveOptions(maxIsolation));

    // Generate a set of Far::PatchTable that we will use to evaluate the
    // surface limit
    Far::PatchTableFactory::Options patchOptions;
    patchOptions.endCapType =
        Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;

    Far::PatchTable const* patchTable =
        Far::PatchTableFactory::Create(*refiner, patchOptions);

    // Compute the total number of points we need to evaluate patchtable.
    // we use local points around extraordinary features.
    int nRefinerVertices = refiner->GetNumVerticesTotal();
    int nLocalPoints = patchTable->GetNumLocalPoints();

    // Create a buffer to hold the position of the refined verts and
    // local points, then copy the coarse positions at the beginning.
    std::vector<Vertex> verts(nRefinerVertices + nLocalPoints);
    memcpy(&verts[0], g_verts, g_nverts * 3 * sizeof(double));

    // Adaptive refinement may result in fewer levels than maxIsolation.
    int nRefinedLevels = refiner->GetNumLevels();

    // Interpolate vertex primvar data : they are the control vertices
    // of the limit patches (see far_tutorial_0 for details)
    Vertex* src = &verts[0];
    for (int level = 1; level < nRefinedLevels; ++level)
    {
        Vertex* dst = src + refiner->GetLevel(level - 1).GetNumVertices();
        Far::PrimvarRefiner(*refiner).Interpolate(level, src, dst);
        src = dst;
    }

    // Evaluate local points from interpolated vertex primvars.
    patchTable->ComputeLocalPointValues(&verts[0], &verts[nRefinerVertices]);

    std::vector<TopoDS_Face> mySurfaces;

    // Loop through each patch and save out 4x4 vertices each
    int na = patchTable->GetNumPatchArrays();
    bool error;
    for (int i = 0; i < na; i++)
    {
        Far::PatchDescriptor pd = patchTable->GetPatchArrayDescriptor(i);
        if (pd == 6) // Type::REGULAR
        {
            Far::ConstIndexArray arraycvs = patchTable->GetPatchArrayVertices(i);
            int np = patchTable->GetNumPatches(i);

            for (int patch = 0; patch < np; patch++)
            {
                Far::ConstIndexArray cvs = patchTable->GetPatchVertices(i, patch);
                int cvCount = cvs.size();
                TColgp_Array2OfPnt surfVerts(1, 4, 1, 4);

                for (int cv = 0; cv < cvCount; cv++)
                {
                    int division = (int)((cv + 1) / 4);
                    int remainder = (cv + 1) % 4;
                    int firstIndex = remainder == 0 ? division : division + 1;
                    int secondIndex = remainder == 0 ? 4 : remainder;
                    surfVerts.SetValue(firstIndex, secondIndex, gp_Pnt(verts[cvs[cv]].point[0], verts[cvs[cv]].point[1], verts[cvs[cv]].point[2]));
                }

                Handle(Geom_BezierSurface) BZ1 =
                    new Geom_BezierSurface(surfVerts);

                TopoDS_Face newFace = BRepBuilderAPI_MakeFace(BZ1, Precision::Confusion());
                mySurfaces.push_back(newFace);
            }
        }
    }

    for (int i = 0; i < mySurfaces.size(); i++)
    {
        Quantity_NameOfColor myColor = static_cast<Quantity_NameOfColor>((i % 505) + 1);
        Handle(AIS_Shape) myFace = new AIS_Shape(mySurfaces[i]);
        myAISContext->SetColor(myFace, myColor, Standard_False);
        myAISContext->SetMaterial(myFace, Graphic3d_NOM_PLASTIC, Standard_False);
        myAISContext->Display(myFace, Standard_False);
    }
}
rsp
  • 537
  • 2
  • 13

1 Answers1

1

Somewhere in that code you have the wrong knot vector (I posted the original question in the thread you referenced). From the Google Groups thread:

Ah, figured out that my knot vector was the issue:

{ 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 };

What I needed was:

{ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0 };

  • Thank you Greg, I was able to resolve this wih the help of Bruce Malone's suggestion. The problem was that I was using Bezier instead of B-Spline for patches so it was giving incorrect result. But thanks anyway for your suggestion. Hope you are making progress in your project. – rsp Aug 08 '22 at 10:48