Rendering issues with HdBasisCurvesSchema and Generative Procedural plugin

Hi,

I’m fairly new to USD so apologize in advance if I use the wrong terminology. I’m investigating how hard it would be to write a Generative Procedural plugin to generate hair from our custom hair tool at work.

The Procedural Mesh Generation topic was really helpful to get me started. I based my plugin on the _CubePerMeshPointProcedural example in the source of USD. Here is a stripped out version of the code I have:

class _CurveHdGpProcedural final: public HdGpGenerativeProcedural
{
public:
    //…
    auto Update(
        const HdSceneIndexBaseRefPtr& inputScene,
        const ChildPrimTypeMap& previousResult,
        const DependencyMap& dirtiedDependencies,
        HdSceneIndexObserver::DirtiedPrimEntries* outputDirtiedPrims)
        -> ChildPrimTypeMap override
    {
        ChildPrimTypeMap result;

        // Populate m_num_vertices, m_indices etc
        // …

        // Tell GetChildPrim() to generate the schema
        m_load_data = true;

        const SdfPath path = _GetProceduralPrimPath();
        const SdfPath curves_path = path.AppendChild(TfToken("Curves"));
        result[curves_path] = HdPrimTypeTokens->basisCurves;
        if (outputDirtiedPrims)
        {
            outputDirtiedPrims->emplace_back(
                curves_path,
                HdBasisCurvesSchema::GetDefaultLocator());
        }

        return result;
    }

    // Returns dataSource of a child prim
    auto GetChildPrim(
        const HdSceneIndexBaseRefPtr& inputScene,
        const SdfPath& childPrimPath) -> HdSceneIndexPrim override
    {
        HdSceneIndexPrim result;

        if (m_load_data)
        {
            result.primType = HdPrimTypeTokens->basisCurves;
            result.dataSource = HdRetainedContainerDataSource::New(
                HdBasisCurvesSchemaTokens->basisCurves,
                getCurvesTopology(),
                HdPrimvarsSchemaTokens->primvars,
                getCurvesPrimvars());
            m_load_data = false;
        }

        return result;
    }
private:
    auto getCurvesTopology() const -> HdContainerDataSourceHandle
    {
        using _IntArrayDataSource
            = HdRetainedTypedSampledDataSource<VtIntArray>;
        using _TokenDataSource = HdRetainedTypedSampledDataSource<TfToken>;

        static const _IntArrayDataSource::Handle vertex_count_data_source
            = _IntArrayDataSource::New(m_num_vertices);

        static const _IntArrayDataSource::Handle indices_data_source
            = _IntArrayDataSource::New(m_indices);

        static const _TokenDataSource::Handle basis_data_source
            = _TokenDataSource::New(HdTokens->catmullRom);

        static const _TokenDataSource::Handle type_data_source
            = _TokenDataSource::New(HdTokens->linear);

        static const _TokenDataSource::Handle wrap_data_source
            = _TokenDataSource::New(HdTokens->nonperiodic);

        static const HdContainerDataSourceHandle curves_data_source
            = HdBasisCurvesSchema::Builder()
                  .SetTopology(
                      HdBasisCurvesTopologySchema::Builder()
                          .SetCurveVertexCounts(vertex_count_data_source)
                          .SetCurveIndices(indices_data_source)
                          .SetBasis(basis_data_source)
                          .SetType(type_data_source)
                          .SetWrap(wrap_data_source)
                          .Build())
                  .Build();

        return curves_data_source;
    }

    auto getCurvesPrimvars() const -> HdContainerDataSourceHandle
    {
        using _PointDataSource
            = HdRetainedTypedSampledDataSource<VtVec3fArray>;
        using _FloatDataSource
            = HdRetainedTypedSampledDataSource<VtFloatArray>;

        static const HdContainerDataSourceHandle primvars_data_source
            = HdRetainedContainerDataSource::New(
                HdPrimvarsSchemaTokens->points,
                HdPrimvarSchema::Builder()
                    .SetPrimvarValue(_PointDataSource::New(m_points))
                    .SetInterpolation(
                        HdPrimvarSchema::BuildInterpolationDataSource(
                            HdPrimvarSchemaTokens->uniform))
                    .SetRole(HdPrimvarSchema::BuildRoleDataSource(
                        HdPrimvarSchemaTokens->point))
                    .Build(),
                HdPrimvarsSchemaTokens->widths,
                HdPrimvarSchema::Builder()
                    .SetPrimvarValue(_FloatDataSource::New(m_widths))
                    .SetInterpolation(
                        HdPrimvarSchema::BuildInterpolationDataSource(
                            HdPrimvarSchemaTokens->uniform))
                    .Build());

        return primvars_data_source;
    }

private: // Member variables
    bool m_load_data = true;
    VtIntArray m_num_vertices;
    VtIntArray m_indices;
    VtVec3fArray m_points;
    VtFloatArray m_widths;
};

I can draw a box with my plugin if I switch to using HdMeshSchema, construct it similar to what they do in _CubePerMeshPointProcedural::_GetChildMeshDs() and _CubePerMeshPointProcedural::_GetChildPrimvarsDs. So I know I got the plugin hooked up correctly.

I don’t get any errors when running this with HdBasisCurvesSchema. My suspicion is that I missing something when building the schema for the curves. As I can get similar behavior if removing the HdPrimvarSchema::BuildInterpolationDataSource when testing with a mesh.

I have tested to just to hard code a basic curve for testing in case something is wrong with how I read in the data form the hair tool:

    VtIntArray m_num_vertices{4};
    VtIntArray m_indices{0, 1, 2, 3};
    VtVec3fArray m_points{
        {0.0, 0.0, 0.0},
        {0.0, 1.0, 0.0},
        {0.0, 2.0, 0.0},
        {0.0, 3.0, 0.0},
    };
    VtFloatArray m_widths{0.1, 0.2, 0.2, 0.2};

Same issue.

Setting TF_DEBUG="*" doesn’t give any clues either.

Anyone know what I’m doing wrong/missing?

Thanks.

I don’t have a specific answer to your situation, but as a general debugging step, I find the Hydra Scene Browser widget in usdview to be invaluable for inspecting what data is/isn’t being generated, and comparing the data from an HdGp plugin (or other Scene Index plugins) to that generated via Pixar’s USD → Hydra conversion. So, for example, put an explicit USD curve in your scene and use the Hydra Scene Browser to compare that curve’s Hydra data against the data coming from your procedural.

1 Like

Hi Rob,

Thank you for both recommending Hydra Scene Browser and use an explicit USD curve. With those I finally figured out the issue!

I had misunderstood how the GetChildPrim was working so my cache check with m_load_data did the opposite and did not load the data in Hydra. Removing that and everything worked.

Reason why it worked with the mesh was that I had a “bug” in that test code that skip setting m_load_data to false. Using the Hydra Scene Browser made that pretty obvious.

Here is how my final GetChildPrim function looks like:

    auto GetChildPrim(
        const HdSceneIndexBaseRefPtr& inputScene,
        const SdfPath& childPrimPath) -> HdSceneIndexPrim override
    {
        HdSceneIndexPrim result;

        result.primType = HdPrimTypeTokens->basisCurves;
        result.dataSource = HdRetainedContainerDataSource::New(
            HdBasisCurvesSchemaTokens->basisCurves,
            getCurvesTopology(),
            HdPrimvarsSchemaTokens->primvars,
            getCurvesPrimvars());

        return result;
    }
1 Like