//-*****************************************************************************
Box3d getBounds( IObject iObj )
{
    Box3d bnds;
    bnds.makeEmpty();

    M44d xf = getFinalMatrix( iObj );

    if ( IPolyMesh::matches( iObj.getMetaData() ) )
    {
        IPolyMesh mesh( iObj, kWrapExisting );
        IPolyMeshSchema ms = mesh.getSchema();
        V3fArraySamplePtr positions = ms.getValue().getPositions();
        size_t numPoints = positions->size();

        for ( size_t i = 0 ; i < numPoints ; ++i )
        {
            bnds.extendBy( (*positions)[i] );
        }
    }
    else if ( ISubD::matches( iObj.getMetaData() ) )
    {
        ISubD mesh( iObj, kWrapExisting );
        ISubDSchema ms = mesh.getSchema();
        V3fArraySamplePtr positions = ms.getValue().getPositions();
        size_t numPoints = positions->size();

        for ( size_t i = 0 ; i < numPoints ; ++i )
        {
            bnds.extendBy( (*positions)[i] );
        }
    }

    bnds.extendBy( Imath::transform( bnds, xf ) );

    g_bounds.extendBy( bnds );

    return bnds;
}
//-*****************************************************************************
void MeshDrwHelper::update( V3fArraySamplePtr iP,
                            V3fArraySamplePtr iN )
{
    // Check validity.
    if ( !m_valid || !iP || !m_meshP ||
            ( iP->size() != m_meshP->size() ) )
    {
        makeInvalid();
        return;
    }

    // Set meshP
    m_meshP = iP;
    computeBounds();
    updateNormals( iN );
}
void readV3fArrayProperty(const std::string &archiveName)
{
    // Open an existing archive for reading. Indicate that we want
    //   Alembic to throw exceptions on errors.
    std::cout  << "Reading " << archiveName << std::endl;
    IArchive archive( Alembic::AbcCoreHDF5::ReadArchive(),
                      archiveName, ErrorHandler::kThrowPolicy );
    IObject archiveTop = archive.getTop();

    // Determine the number of (top level) children the archive has
    const unsigned int numChildren =  archiveTop.getNumChildren();
    ABCA_ASSERT( numChildren == 1, "Wrong number of children (expected 1)");
    std::cout << "The archive has " << numChildren << " children:"
              << std::endl;

    // Iterate through them, print out their names
    IObject child( archiveTop, archiveTop.getChildHeader(0).getName() );
    std::cout << "  named '" << child.getName() << "'";

    // Properties
    ICompoundProperty props = child.getProperties();
    size_t numProperties = props.getNumProperties();  // only top-level props
    ABCA_ASSERT( numProperties == 1,
                 "Expected 1 property, found " << numProperties);
    std::cout << " with one property";

    std::vector<std::string> propNames(1);
    propNames[0] = props.getPropertyHeader(0).getName();
    std::cout << " named '" << propNames[0] << "'" << std::endl;


    PropertyType pType = props.getPropertyHeader(0).getPropertyType();
    ABCA_ASSERT( pType == kArrayProperty,
                 "Expected an array property, but didn't find one" );

    std::cout << " which is an array property";

    DataType dType = props.getPropertyHeader(0).getDataType();
    ABCA_ASSERT( dType.getPod() == kFloat32POD,
                 "Expected an v3f property, but didn't find one" );


    // We know this is an array property (I'm eliding the if/else
    //  statements required to recognize and handle this properly)
    IV3fArrayProperty positions( props, propNames[0] );
    size_t numSamples = positions.getNumSamples();
    std::cout << ".. it has " << numSamples << " samples" << std::endl;
    ABCA_ASSERT( numSamples == 5, "Expected 5 samples, found " << numSamples );

    const TimeSampling ts = positions.getTimeSampling();
    std::cout << "..with time/value pairs: " << std::endl;;
    for (int ss=0; ss<numSamples; ss++)
    {
        std::cout << "   ";
        ISampleSelector iss( (index_t) ss);
        std::cout << ts.getSampleTime( (index_t) ss ) << " / ";

        V3fArraySamplePtr samplePtr;
        positions.get( samplePtr, iss );
        std::cout << "[ ";
        size_t numPoints = samplePtr->size();
        for ( size_t jj=0 ; jj<numPoints ; jj++ )
            std::cout << (*samplePtr)[jj] << " ";
        std::cout << "]" << std::endl;

        if (ss == 2) // no entries in sample #2
        {
            ABCA_ASSERT( numPoints == 0,
                         "Expected an empty sample, but found " << numPoints
                         << " entries." );
        }
        else
        {
            for ( size_t jj=0 ; jj<numPoints ; jj++ )
                ABCA_ASSERT( (*samplePtr)[jj] == g_vectors[jj],
                             "Incorrect value read from archive." );
        }

    }
    std::cout << std::endl;
    // Done - the archive closes itself
}
//-*****************************************************************************
void MeshDrwHelper::update( V3fArraySamplePtr iP,
                            V3fArraySamplePtr iN,
                            Int32ArraySamplePtr iIndices,
                            Int32ArraySamplePtr iCounts )
{
    // Before doing a ton, just have a quick look.
    if ( m_meshP && iP &&
            ( m_meshP->size() == iP->size() ) &&
            m_meshIndices &&
            ( m_meshIndices == iIndices ) &&
            m_meshCounts &&
            ( m_meshCounts == iCounts ) )
    {
        if ( m_meshP == iP )
        {
            updateNormals( iN );
        }
        else
        {
            update( iP, iN );
        }
        return;
    }

    // Okay, if we're here, the indices are not equal or the counts
    // are not equal or the P-array size changed.
    // So we can clobber those three, but leave N alone for now.
    m_meshP = iP;
    m_meshIndices = iIndices;
    m_meshCounts = iCounts;

    // Check stuff.
    if ( !m_meshP ||
            !m_meshIndices ||
            !m_meshCounts )
    {
        std::cerr << "Mesh update quitting because no input data"
                  << std::endl;
        makeInvalid();
        return;
    }

    // Get the number of each thing.
    size_t numFaces = m_meshCounts->size();
    size_t numIndices = m_meshIndices->size();
    size_t numPoints = m_meshP->size();
    if ( numFaces < 1 ||
            numIndices < 1 ||
            numPoints < 1 )
    {
        // Invalid.
        std::cerr << "Mesh update quitting because bad arrays"
                  << ", numFaces = " << numFaces
                  << ", numIndices = " << numIndices
                  << ", numPoints = " << numPoints
                  << std::endl;
        makeInvalid();
        return;
    }

    // Make triangles.
    size_t faceIndexBegin = 0;
    size_t faceIndexEnd = 0;
    for ( size_t face = 0; face < numFaces; ++face )
    {
        faceIndexBegin = faceIndexEnd;
        size_t count = (*m_meshCounts)[face];
        faceIndexEnd = faceIndexBegin + count;

        // Check this face is valid
        if ( faceIndexEnd > numIndices ||
                faceIndexEnd < faceIndexBegin )
        {
            std::cerr << "Mesh update quitting on face: "
                      << face
                      << " because of wonky numbers"
                      << ", faceIndexBegin = " << faceIndexBegin
                      << ", faceIndexEnd = " << faceIndexEnd
                      << ", numIndices = " << numIndices
                      << ", count = " << count
                      << std::endl;

            // Just get out, make no more triangles.
            break;
        }

        // Checking indices are valid.
        bool goodFace = true;
        for ( size_t fidx = faceIndexBegin;
                fidx < faceIndexEnd; ++fidx )
        {
            if ( (*m_meshIndices)[fidx] >= numPoints )
            {
                std::cout << "Mesh update quitting on face: "
                          << face
                          << " because of bad indices"
                          << ", indexIndex = " << fidx
                          << ", vertexIndex = " << (*m_meshIndices)[fidx]
                          << ", numPoints = " << numPoints
                          << std::endl;
                goodFace = false;
                break;
            }
        }

        // Make triangles to fill this face.
        if ( goodFace && count > 2 )
        {
            m_triangles.push_back(
                Tri( ( unsigned int )(*m_meshIndices)[faceIndexBegin+0],
                     ( unsigned int )(*m_meshIndices)[faceIndexBegin+1],
                     ( unsigned int )(*m_meshIndices)[faceIndexBegin+2] ) );
            for ( size_t c = 3; c < count; ++c )
            {
                m_triangles.push_back(
                    Tri( ( unsigned int )(*m_meshIndices)[faceIndexBegin+0],
                         ( unsigned int )(*m_meshIndices)[faceIndexBegin+c-1],
                         ( unsigned int )(*m_meshIndices)[faceIndexBegin+c]
                       ) );
            }
        }
    }

    // Cool, we made triangles.
    // Pretend the mesh is made...
    m_valid = true;

    // And now update just the P and N, which will update bounds
    // and calculate new normals if necessary.
    computeBounds();
    updateNormals( iN );

    // And that's it.
}