LimitStencilTables const *
LimitStencilTablesFactory::Create(TopologyRefiner const & refiner,
    LocationArrayVec const & locationArrays, StencilTables const * cvStencils,
        PatchTables const * patchTables) {

    // Compute the total number of stencils to generate
    int numStencils=0, numLimitStencils=0;
    for (int i=0; i<(int)locationArrays.size(); ++i) {
        assert(locationArrays[i].numLocations>=0);
        numStencils += locationArrays[i].numLocations;
    }
    if (numStencils<=0) {
        return 0;
    }

    bool uniform = refiner.IsUniform();

    int maxlevel = refiner.GetMaxLevel(), maxsize=17;

    StencilTables const * cvstencils = cvStencils;
    if (not cvstencils) {
        // Generate stencils for the control vertices - this is necessary to
        // properly factorize patches with control vertices at level 0 (natural
        // regular patches, such as in a torus)
        // note: the control vertices of the mesh are added as single-index
        //       stencils of weight 1.0f
        StencilTablesFactory::Options options;
        options.generateIntermediateLevels = uniform ? false :true;
        options.generateControlVerts = true;
        options.generateOffsets = true;

        // XXXX (manuelk) We could potentially save some mem-copies by not
        // instanciating the stencil tables and work directly off the pool
        // allocators.
        cvstencils = StencilTablesFactory::Create(refiner, options);
    } else {
        // Sanity checks
        if (cvstencils->GetNumStencils() != (uniform ?
            refiner.GetNumVertices(maxlevel) :
                refiner.GetNumVerticesTotal())) {
                return 0;
        }
    }

    // If a stencil table was given, use it, otherwise, create a new one
    PatchTables const * patchtables = patchTables;
    if (not patchTables) {
        // XXXX (manuelk) If no patch-tables was passed, we should be able to
        // infer the patches fairly easily from the refiner. Once more tags
        // have been added to the refiner, maybe we can remove the need for the
        // patch tables.

        OpenSubdiv::Far::PatchTablesFactory::Options options;
        options.adaptiveStencilTables = cvstencils;

        patchtables = PatchTablesFactory::Create(refiner, options);
    } else {
        // Sanity checks
        if (patchTables->IsFeatureAdaptive()==uniform) {
            if (not cvStencils) {
                assert(cvstencils and cvstencils!=cvStencils);
                delete cvstencils;
            }
            return 0;
        }
    }

    assert(patchtables and cvstencils);

    // Create a patch-map to locate sub-patches faster
    PatchMap patchmap( *patchtables );

    //
    // Generate limit stencils for locations
    //

    // Create a pool allocator to accumulate ProtoLimitStencils
    LimitStencilAllocator alloc(maxsize);
    alloc.Resize(numStencils);

    // XXXX (manuelk) we can make uniform (bilinear) stencils faster with a
    //       dedicated code path that does not use PatchTables or the PatchMap
    for (int i=0, currentStencil=0; i<(int)locationArrays.size(); ++i) {

        LocationArray const & array = locationArrays[i];

        assert(array.ptexIdx>=0);

        for (int j=0; j<array.numLocations; ++j, ++currentStencil) {

            float s = array.s[j],
                  t = array.t[j];

            PatchMap::Handle const * handle =
                patchmap.FindPatch(array.ptexIdx, s, t);

            if (handle) {
                ProtoLimitStencil dst = alloc[currentStencil];
                if (uniform) {
                    patchtables->Interpolate(*handle, s, t, *cvstencils, dst);
                } else {
                    patchtables->Limit(*handle, s, t, *cvstencils, dst);
                }
                ++numLimitStencils;
            }
        }
    }

    if (not cvStencils) {
        delete cvstencils;
    }

    //
    // Copy the proto-stencils into the limit stencil tables
    //
    LimitStencilTables * result = new LimitStencilTables;

    int nelems = alloc.GetNumVerticesTotal();
    if (nelems>0) {

        // Allocate
        result->resize(numLimitStencils, nelems);

        // Copy stencils
        LimitStencil dst(&result->_sizes.at(0), &result->_indices.at(0),
            &result->_weights.at(0), &result->_duWeights.at(0),
                &result->_dvWeights.at(0));

        for (int i=0; i<alloc.GetNumStencils(); ++i) {
            *dst._size = alloc.CopyLimitStencil(i, dst._indices, dst._weights,
                dst._duWeights, dst._dvWeights);
            dst.Next();
        }

        // XXXX manuelk should offset creation be optional ?
        result->generateOffsets();
    }
    result->_numControlVertices = refiner.GetNumVertices(0);

    return result;
}
//------------------------------------------------------------------------------
LimitStencilTable const *
LimitStencilTableFactory::Create(TopologyRefiner const & refiner,
    LocationArrayVec const & locationArrays,
        StencilTable const * cvStencilsIn,
          PatchTable const * patchTableIn,
                     Options options) {

    // Compute the total number of stencils to generate
    int numStencils=0, numLimitStencils=0;
    for (int i=0; i<(int)locationArrays.size(); ++i) {
        assert(locationArrays[i].numLocations>=0);
        numStencils += locationArrays[i].numLocations;
    }
    if (numStencils<=0) {
        return 0;
    }

    bool uniform = refiner.IsUniform();

    int maxlevel = refiner.GetMaxLevel();

    StencilTable const * cvstencils = cvStencilsIn;
    if (! cvstencils) {
        // Generate stencils for the control vertices - this is necessary to
        // properly factorize patches with control vertices at level 0 (natural
        // regular patches, such as in a torus)
        // note: the control vertices of the mesh are added as single-index
        //       stencils of weight 1.0f
        StencilTableFactory::Options options;
        options.generateIntermediateLevels = uniform ? false :true;
        options.generateControlVerts = true;
        options.generateOffsets = true;

        // PERFORMANCE: We could potentially save some mem-copies by not
        // instantiating the stencil tables and work directly off the source
        // data.
        cvstencils = StencilTableFactory::Create(refiner, options);
    } else {
        // Sanity checks
        //
        // Note that the input cvStencils could be larger than the number of
        // refiner's vertices, due to the existence of the end cap stencils.
        if (cvstencils->GetNumStencils() < (uniform ?
            refiner.GetLevel(maxlevel).GetNumVertices() :
                refiner.GetNumVerticesTotal())) {
                return 0;
        }
    }

    // If a stencil table was given, use it, otherwise, create a new one
    PatchTable const * patchtable = patchTableIn;

    if (! patchtable) {
        // XXXX (manuelk) If no patch-table was passed, we should be able to
        // infer the patches fairly easily from the refiner. Once more tags
        // have been added to the refiner, maybe we can remove the need for the
        // patch table.

        PatchTableFactory::Options options;
        options.SetEndCapType(
            Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS);

        patchtable = PatchTableFactory::Create(refiner, options);

        if (! cvStencilsIn) {
            // if cvstencils is just created above, append endcap stencils
            if (StencilTable const *localPointStencilTable =
                patchtable->GetLocalPointStencilTable()) {
                StencilTable const *table =
                    StencilTableFactory::AppendLocalPointStencilTable(
                        refiner, cvstencils, localPointStencilTable);
                delete cvstencils;
                cvstencils = table;
            }
        }
    } else {
        // Sanity checks
        if (patchtable->IsFeatureAdaptive()==uniform) {
            if (! cvStencilsIn) {
                assert(cvstencils && cvstencils!=cvStencilsIn);
                delete cvstencils;
            }
            return 0;
        }
    }

    assert(patchtable && cvstencils);

    // Create a patch-map to locate sub-patches faster
    PatchMap patchmap( *patchtable );

    //
    // Generate limit stencils for locations
    //

    internal::StencilBuilder builder(refiner.GetLevel(0).GetNumVertices(),
                                /*genControlVerts*/ false,
                                /*compactWeights*/  true);
    internal::StencilBuilder::Index origin(&builder, 0);
    internal::StencilBuilder::Index dst = origin;

    float wP[20], wDs[20], wDt[20], wDss[20], wDst[20], wDtt[20];

    for (size_t i=0; i<locationArrays.size(); ++i) {
        LocationArray const & array = locationArrays[i];
        assert(array.ptexIdx>=0);

        for (int j=0; j<array.numLocations; ++j) { // for each face we're working on
            float s = array.s[j],
                  t = array.t[j]; // for each target (s,t) point on that face

            PatchMap::Handle const * handle = 
                                        patchmap.FindPatch(array.ptexIdx, s, t);
            if (handle) {
                ConstIndexArray cvs = patchtable->GetPatchVertices(*handle);

                StencilTable const & src = *cvstencils;
                dst = origin[numLimitStencils];

                if (options.generate2ndDerivatives) {
                    patchtable->EvaluateBasis(*handle, s, t, wP, wDs, wDt, wDss, wDst, wDtt);

                    dst.Clear();
                    for (int k = 0; k < cvs.size(); ++k) {
                        dst.AddWithWeight(src[cvs[k]], wP[k], wDs[k], wDt[k], wDss[k], wDst[k], wDtt[k]);
                    }
                } else if (options.generate1stDerivatives) {
                    patchtable->EvaluateBasis(*handle, s, t, wP, wDs, wDt);

                    dst.Clear();
                    for (int k = 0; k < cvs.size(); ++k) {
                        dst.AddWithWeight(src[cvs[k]], wP[k], wDs[k], wDt[k]);
                    }
                } else {
                    patchtable->EvaluateBasis(*handle, s, t, wP);

                    dst.Clear();
                    for (int k = 0; k < cvs.size(); ++k) {
                        dst.AddWithWeight(src[cvs[k]], wP[k]);
                    }
                }

                ++numLimitStencils;
            }
        }
    }

    if (! cvStencilsIn) {
        delete cvstencils;
    }

    if (! patchTableIn) {
        delete patchtable;
    }

    //
    // Copy the proto-stencils into the limit stencil table
    //
    LimitStencilTable * result = new LimitStencilTable(
                                          refiner.GetLevel(0).GetNumVertices(),
                                          builder.GetStencilOffsets(),
                                          builder.GetStencilSizes(),
                                          builder.GetStencilSources(),
                                          builder.GetStencilWeights(),
                                          builder.GetStencilDuWeights(),
                                          builder.GetStencilDvWeights(),
                                          builder.GetStencilDuuWeights(),
                                          builder.GetStencilDuvWeights(),
                                          builder.GetStencilDvvWeights(),
                                          /*ctrlVerts*/false,
                                          /*fristOffset*/0);
    return result;
}