bool MayaMeshWriter::_createUVPrimVar(
        UsdGeomGprim &primSchema,
        const TfToken& name,
        const VtArray<GfVec2f>& data,
        const TfToken& interpolation,
        const VtArray<int>& assignmentIndices,
        const int unassignedValueIndex)
{
    unsigned int numValues = data.size();
    if (numValues == 0) {
        return false;
    }

    TfToken interp = interpolation;
    if (numValues == 1 && interp == UsdGeomTokens->constant) {
        interp = TfToken();
    }

    UsdGeomPrimvar primVar =
        primSchema.CreatePrimvar(name,
                                 SdfValueTypeNames->Float2Array,
                                 interp);

    primVar.Set(data);

    if (!assignmentIndices.empty()) {
        primVar.SetIndices(assignmentIndices);
        if (unassignedValueIndex != primVar.GetUnauthoredValuesIndex()) {
           primVar.SetUnauthoredValuesIndex(unassignedValueIndex);
        }
    }

    return true;
}
Beispiel #2
0
static void
_StringFromVtArray(
    string *valueStr,
    const VtArray<T> &valArray)
{
    valueStr->append("[");
    if (typename VtArray<T>::const_pointer d = valArray.cdata()) {
        if (const size_t n = valArray.size()) {
            valueStr->append(_StringFromValue(d[0]));
            for (size_t i = 1; i != n; ++i) {
                valueStr->append(", ");
                valueStr->append(_StringFromValue(d[i]));
            }
        }
    }
    valueStr->append("]");
}
bool MayaMeshWriter::_createRGBAPrimVar(
        UsdGeomGprim &primSchema,
        const TfToken& name,
        const VtArray<GfVec3f>& rgbData,
        const VtArray<float>& alphaData,
        const TfToken& interpolation,
        const VtArray<int>& assignmentIndices,
        const int unassignedValueIndex,
        bool clamped)
{
    unsigned int numValues = rgbData.size();
    if (numValues == 0 || numValues != alphaData.size()) {
        return false;
    }

    TfToken interp = interpolation;
    if (numValues == 1 && interp == UsdGeomTokens->constant) {
        interp = TfToken();
    }

    UsdGeomPrimvar primVar =
        primSchema.CreatePrimvar(name,
                                 SdfValueTypeNames->Color4fArray,
                                 interp);

    VtArray<GfVec4f> rgbaData(numValues);
    for (size_t i = 0; i < rgbaData.size(); ++i) {
        rgbaData[i] = GfVec4f(rgbData[i][0], rgbData[i][1], rgbData[i][2],
                              alphaData[i]);
    }

    primVar.Set(rgbaData);

    if (!assignmentIndices.empty()) {
        primVar.SetIndices(assignmentIndices);
        if (unassignedValueIndex != primVar.GetUnauthoredValuesIndex()) {
           primVar.SetUnauthoredValuesIndex(unassignedValueIndex);
        }
    }

    if (clamped) {
        PxrUsdMayaRoundTripUtil::MarkPrimvarAsClamped(primVar);
    }

    return true;
}
bool MayaMeshWriter::_createRGBPrimVar(
        UsdGeomGprim &primSchema,
        const TfToken& name,
        const VtArray<GfVec3f>& data,
        const TfToken& interpolation,
        const VtArray<int>& assignmentIndices,
        const int unassignedValueIndex,
        bool clamped)
{
    unsigned int numValues = data.size();
    if (numValues == 0) {
        return false;
    }

    TfToken interp = interpolation;
    if (numValues == 1 && interp == UsdGeomTokens->constant) {
        interp = TfToken();
    }

    UsdGeomPrimvar primVar =
        primSchema.CreatePrimvar(name,
                                 SdfValueTypeNames->Color3fArray,
                                 interp);

    primVar.Set(data);

    if (!assignmentIndices.empty()) {
        primVar.SetIndices(assignmentIndices);
        if (unassignedValueIndex != primVar.GetUnauthoredValuesIndex()) {
           primVar.SetUnauthoredValuesIndex(unassignedValueIndex);
        }
    }

    if (clamped) {
        PxrUsdMayaRoundTripUtil::MarkPrimvarAsClamped(primVar);
    }

    return true;
}
Beispiel #5
0
PXR_NAMESPACE_OPEN_SCOPE



/* static */
bool 
PxrUsdMayaTranslatorMesh::Create(
        const UsdGeomMesh& mesh,
        MObject parentNode,
        const PxrUsdMayaPrimReaderArgs& args,
        PxrUsdMayaPrimReaderContext* context)
{
    if (!mesh) {
        return false;
    }

    const UsdPrim& prim = mesh.GetPrim();

    MStatus status;

    // Create node (transform)
    MObject mayaNodeTransformObj;
    if (!PxrUsdMayaTranslatorUtil::CreateTransformNode(prim,
                                                          parentNode,
                                                          args,
                                                          context,
                                                          &status,
                                                          &mayaNodeTransformObj)) {
        return false;
    }

    VtArray<GfVec3f> points;
    VtArray<GfVec3f> normals;
    VtArray<int>     faceVertexCounts;
    VtArray<int>     faceVertexIndices;
    
    UsdAttribute fvc = mesh.GetFaceVertexCountsAttr();
    if (fvc.ValueMightBeTimeVarying()){
        // at some point, it would be great, instead of failing, to create a usd/hydra proxy node
        // for the mesh, perhaps?  For now, better to give a more specific error
        MGlobal::displayError(
            TfStringPrintf("<%s> is a topologically varying Mesh (animated faceVertexCounts). Skipping...", 
                prim.GetPath().GetText()).c_str());
        return false;
    } else {
        // for any non-topo-varying mesh, sampling at zero will get us the right answer
        fvc.Get(&faceVertexCounts, 0);
    }

    UsdAttribute fvi = mesh.GetFaceVertexIndicesAttr();
    if (fvi.ValueMightBeTimeVarying()){
        // at some point, it would be great, instead of failing, to create a usd/hydra proxy node
        // for the mesh, perhaps?  For now, better to give a more specific error
        MGlobal::displayError(
            TfStringPrintf("<%s> is a topologically varying Mesh (animated faceVertexIndices). Skipping...", 
                prim.GetPath().GetText()).c_str());
        return false;
    } else {
        // for any non-topo-varying mesh, sampling at zero will get us the right answer
        fvi.Get(&faceVertexIndices, 0);
    }
        
    // Sanity Checks. If the vertex arrays are empty, skip this mesh
    if (faceVertexCounts.size() == 0 || faceVertexIndices.size() == 0) {
        MGlobal::displayError(
            TfStringPrintf("FaceVertex arrays are empty [Count:%zu Indices:%zu] on Mesh <%s>. Skipping...", 
                faceVertexCounts.size(), faceVertexIndices.size(), 
                prim.GetPath().GetText()).c_str());
        return false; // invalid mesh, so exit
    }

    // Gather points and normals
    // If args.GetReadAnimData() is TRUE,
    // pick the first avaiable sample or default
    UsdTimeCode pointsTimeSample=UsdTimeCode::EarliestTime();
    UsdTimeCode normalsTimeSample=UsdTimeCode::EarliestTime();
    std::vector<double> pointsTimeSamples;
    size_t pointsNumTimeSamples = 0;
    if (args.GetReadAnimData()) {
        PxrUsdMayaTranslatorUtil::GetTimeSamples(mesh.GetPointsAttr(), args,
                &pointsTimeSamples);
        pointsNumTimeSamples = pointsTimeSamples.size();
        if (pointsNumTimeSamples>0) {
            pointsTimeSample = pointsTimeSamples[0];
        }
    	std::vector<double> normalsTimeSamples;
        PxrUsdMayaTranslatorUtil::GetTimeSamples(mesh.GetNormalsAttr(), args,
                &normalsTimeSamples);
        if (normalsTimeSamples.size()) {
            normalsTimeSample = normalsTimeSamples[0];
        }
    }
    mesh.GetPointsAttr().Get(&points, pointsTimeSample);
    mesh.GetNormalsAttr().Get(&normals, normalsTimeSample);
    
    if (points.size() == 0) {
        MGlobal::displayError(
            TfStringPrintf("Points arrays is empty on Mesh <%s>. Skipping...", 
                prim.GetPath().GetText()).c_str());
        return false; // invalid mesh, so exit
    }


    // == Convert data
    size_t mayaNumVertices = points.size();
    MPointArray mayaPoints(mayaNumVertices);
    for (size_t i=0; i < mayaNumVertices; i++) {
        mayaPoints.set( i, points[i][0], points[i][1], points[i][2] );
    }

    MIntArray polygonCounts( faceVertexCounts.cdata(),  faceVertexCounts.size() );
    MIntArray polygonConnects( faceVertexIndices.cdata(), faceVertexIndices.size() );

    // == Create Mesh Shape Node
    MFnMesh meshFn;
    MObject meshObj = meshFn.create(mayaPoints.length(), 
                           polygonCounts.length(), 
                           mayaPoints, 
                           polygonCounts, 
                           polygonConnects,
                           mayaNodeTransformObj,
                           &status
                           );
                           
    if (status != MS::kSuccess) {
        return false;
    }

    // Since we are "decollapsing", we will create a xform and a shape node for each USD prim
    std::string usdPrimName(prim.GetName().GetText());
    std::string shapeName(usdPrimName); shapeName += "Shape";
    // Set mesh name and register
    meshFn.setName(MString(shapeName.c_str()), false, &status);
    if (context) {
        std::string usdPrimPath(prim.GetPath().GetText());
        std::string shapePath(usdPrimPath);
        shapePath += "/";
        shapePath += shapeName;
        context->RegisterNewMayaNode( shapePath, meshObj ); // used for undo/redo
    }

    // If a material is bound, create (or reuse if already present) and assign it
    // If no binding is present, assign the mesh to the default shader    
    const TfToken& shadingMode = args.GetShadingMode();
    PxrUsdMayaTranslatorMaterial::AssignMaterial(shadingMode, mesh, meshObj,
            context);
    
    // Mesh is a shape, so read Gprim properties
    PxrUsdMayaTranslatorGprim::Read(mesh, meshObj, context);

    // Set normals if supplied
    MIntArray normalsFaceIds;
    if (normals.size() == static_cast<size_t>(meshFn.numFaceVertices())) {

        for (size_t i=0; i < polygonCounts.length(); i++) {
            for (int j=0; j < polygonCounts[i]; j++) {
                normalsFaceIds.append(i);
            }
        }
        if (normalsFaceIds.length() == static_cast<size_t>(meshFn.numFaceVertices())) {
            MVectorArray mayaNormals(normals.size());
            for (size_t i=0; i < normals.size(); i++) {
                mayaNormals.set( MVector(normals[i][0], normals[i][1], normals[i][2]), i);
            }
            if (meshFn.setFaceVertexNormals(mayaNormals, normalsFaceIds, polygonConnects) != MS::kSuccess) {
            }
        }
     }

    // Determine if PolyMesh or SubdivMesh
    TfToken subdScheme = PxrUsdMayaMeshUtil::setSubdivScheme(mesh, meshFn, args.GetDefaultMeshScheme());

    // If we are dealing with polys, check if there are normals
    // If we are dealing with SubdivMesh, read additional attributes and SubdivMesh properties
    if (subdScheme == UsdGeomTokens->none) {
        if (normals.size() == static_cast<size_t>(meshFn.numFaceVertices())) {
            PxrUsdMayaMeshUtil::setEmitNormals(mesh, meshFn, UsdGeomTokens->none);
        }
    } else {
        PxrUsdMayaMeshUtil::setSubdivInterpBoundary(mesh, meshFn, UsdGeomTokens->edgeAndCorner);
        PxrUsdMayaMeshUtil::setSubdivFVLinearInterpolation(mesh, meshFn);
        _AssignSubDivTagsToMesh(mesh, meshObj, meshFn);
    }
 
    // Set Holes
    VtArray<int> holeIndices;
    mesh.GetHoleIndicesAttr().Get(&holeIndices);   // not animatable
    if ( holeIndices.size() != 0 ) {
        MUintArray mayaHoleIndices;
        mayaHoleIndices.setLength( holeIndices.size() );
        for (size_t i=0; i < holeIndices.size(); i++) {
            mayaHoleIndices[i] = holeIndices[i];
        }
        if (meshFn.setInvisibleFaces(mayaHoleIndices) == MS::kFailure) {
            MGlobal::displayError(TfStringPrintf("Unable to set Invisible Faces on <%s>", 
                            meshFn.fullPathName().asChar()).c_str());
        }
    }

    // GETTING PRIMVARS
    std::vector<UsdGeomPrimvar> primvars = mesh.GetPrimvars();
    TF_FOR_ALL(iter, primvars) {
        const UsdGeomPrimvar& primvar = *iter;
        const TfToken& name = primvar.GetBaseName();
        const SdfValueTypeName& typeName = primvar.GetTypeName();

        // If the primvar is called either displayColor or displayOpacity check
        // if it was really authored from the user.  It may not have been
        // authored by the user, for example if it was generated by shader
        // values and not an authored colorset/entity.
        // If it was not really authored, we skip the primvar.
        if (name == PxrUsdMayaMeshColorSetTokens->DisplayColorColorSetName || 
                name == PxrUsdMayaMeshColorSetTokens->DisplayOpacityColorSetName) {
            if (!PxrUsdMayaRoundTripUtil::IsAttributeUserAuthored(primvar)) {
                continue;
            }
        }

        // XXX: Maya stores UVs in MFloatArrays and color set data in MColors
        // which store floats, so we currently only import primvars holding
        // float-typed arrays. Should we still consider other precisions
        // (double, half, ...) and/or numeric types (int)?
        if (typeName == SdfValueTypeNames->Float2Array) {
            // We assume that Float2Array primvars are UV sets.
            if (!_AssignUVSetPrimvarToMesh(primvar, meshFn)) {
                MGlobal::displayWarning(
                    TfStringPrintf("Unable to retrieve and assign data for UV set <%s> on mesh <%s>", 
                                   name.GetText(),
                                   mesh.GetPrim().GetPath().GetText()).c_str());
            }
        } else if (typeName == SdfValueTypeNames->FloatArray   || 
                   typeName == SdfValueTypeNames->Float3Array  || 
                   typeName == SdfValueTypeNames->Color3fArray ||
                   typeName == SdfValueTypeNames->Float4Array  || 
                   typeName == SdfValueTypeNames->Color4fArray) {
            if (!_AssignColorSetPrimvarToMesh(mesh, primvar, meshFn)) {
                MGlobal::displayWarning(
                    TfStringPrintf("Unable to retrieve and assign data for color set <%s> on mesh <%s>",
                                   name.GetText(),
                                   mesh.GetPrim().GetPath().GetText()).c_str());
            }
        }
    }

    // We only vizualize the colorset by default if it is "displayColor".  
    MStringArray colorSetNames;
    if (meshFn.getColorSetNames(colorSetNames)==MS::kSuccess) {
        for (unsigned int i=0; i < colorSetNames.length(); i++) {
            const MString colorSetName = colorSetNames[i];
            if (std::string(colorSetName.asChar()) 
                    == PxrUsdMayaMeshColorSetTokens->DisplayColorColorSetName.GetString()) {
                MFnMesh::MColorRepresentation csRep=
                    meshFn.getColorRepresentation(colorSetName);
                if (csRep==MFnMesh::kRGB || csRep==MFnMesh::kRGBA) {

                    // both of these are needed to show the colorset.
                    MPlug plg=meshFn.findPlug("displayColors");
                    if ( !plg.isNull() ) {
                        plg.setBool(true);
                    }
                    meshFn.setCurrentColorSetName(colorSetName);
                }
                break;
            }
        }
    }
    
    // == Animate points ==
    //   Use blendShapeDeformer so that all the points for a frame are contained in a single node
    //
    if (pointsNumTimeSamples > 0) {
        MPointArray mayaPoints(mayaNumVertices);
        MObject meshAnimObj;

        MFnBlendShapeDeformer blendFn;
        MObject blendObj = blendFn.create(meshObj);
        if (context) {
            context->RegisterNewMayaNode( blendFn.name().asChar(), blendObj ); // used for undo/redo
        }

        for (unsigned int ti=0; ti < pointsNumTimeSamples; ++ti) {
             mesh.GetPointsAttr().Get(&points, pointsTimeSamples[ti]);

            for (unsigned int i=0; i < mayaNumVertices; i++) {
                mayaPoints.set( i, points[i][0], points[i][1], points[i][2] );
            }

            // == Create Mesh Shape Node
            MFnMesh meshFn;
            if ( meshAnimObj.isNull() ) {
                meshAnimObj = meshFn.create(mayaPoints.length(), 
                                       polygonCounts.length(), 
                                       mayaPoints, 
                                       polygonCounts, 
                                       polygonConnects,
                                       mayaNodeTransformObj,
                                       &status
                                       );
                if (status != MS::kSuccess) {
                    continue;
                }
            }
            else {
                // Reuse the already created mesh by copying it and then setting the points
                meshAnimObj = meshFn.copy(meshAnimObj, mayaNodeTransformObj, &status);
                meshFn.setPoints(mayaPoints);
            }

            // Set normals if supplied
            //
            // NOTE: This normal information is not propagated through the blendShapes, only the controlPoints.
            //
             mesh.GetNormalsAttr().Get(&normals, pointsTimeSamples[ti]);
             if (normals.size() == static_cast<size_t>(meshFn.numFaceVertices()) &&
                normalsFaceIds.length() == static_cast<size_t>(meshFn.numFaceVertices())) {

                MVectorArray mayaNormals(normals.size());
                for (size_t i=0; i < normals.size(); i++) {
                    mayaNormals.set( MVector(normals[i][0], normals[i][1], normals[i][2]), i);
                }
                if (meshFn.setFaceVertexNormals(mayaNormals, normalsFaceIds, polygonConnects) != MS::kSuccess) {
                }
             }

            // Add as target and set as an intermediate object
            blendFn.addTarget(meshObj, ti, meshAnimObj, 1.0);
            meshFn.setIntermediateObject(true);
            if (context) {
                context->RegisterNewMayaNode( meshFn.fullPathName().asChar(), meshAnimObj ); // used for undo/redo
            }
        }

        // Animate the weights so that mesh0 has a weight of 1 at frame 0, etc.
        MFnAnimCurve animFn;

        // Construct the time array to be used for all the keys
        MTimeArray timeArray;
        timeArray.setLength(pointsNumTimeSamples);
        for (unsigned int ti=0; ti < pointsNumTimeSamples; ++ti) {
            timeArray.set( MTime(pointsTimeSamples[ti]), ti);
        }

        // Key/Animate the weights
        MPlug plgAry = blendFn.findPlug( "weight" );
        if ( !plgAry.isNull() && plgAry.isArray() ) {
            for (unsigned int ti=0; ti < pointsNumTimeSamples; ++ti) {
                MPlug plg = plgAry.elementByLogicalIndex(ti, &status);
                MDoubleArray valueArray(pointsNumTimeSamples, 0.0);
                valueArray[ti] = 1.0; // Set the time value where this mesh's weight should be 1.0
                MObject animObj = animFn.create(plg, NULL, &status);
                animFn.addKeys(&timeArray, &valueArray);
                if (context) {
                    context->RegisterNewMayaNode(animFn.name().asChar(), animObj ); // used for undo/redo
                }
            }
        }
    }

    return true;
}
Beispiel #6
0
static bool
_ValidateClipFields(
    const VtArray<SdfAssetPath>& clipAssetPaths,
    const std::string& clipPrimPath,
    const VtVec2dArray& clipActive,
    std::string* errMsg)
{
    // Note that we do allow empty clipAssetPath and clipActive data; 
    // this provides users with a way to 'block' clips specified in a 
    // weaker layer.
    if (clipPrimPath.empty()) {
        *errMsg = "No clip prim path specified";
        return false;
    }

    const size_t numClips = clipAssetPaths.size();

    // Each entry in the 'clipAssetPaths' array is the asset path to a clip.
    for (const auto& clipAssetPath : clipAssetPaths) {
        if (clipAssetPath.GetAssetPath().empty()) {
            *errMsg = TfStringPrintf("Empty clip asset path in metadata '%s'",
                                     UsdTokens->clipAssetPaths.GetText());
            return false;
        }
    }

    // The 'clipPrimPath' field identifies a prim from which clip data
    // will be read.
    if (!SdfPath::IsValidPathString(clipPrimPath, errMsg)) {
        return false;
    }
    
    const SdfPath path(clipPrimPath);
    if (!(path.IsAbsolutePath() && path.IsPrimPath())) {
        *errMsg = TfStringPrintf(
            "Path '%s' in metadata '%s' must be an absolute path to a prim",
            clipPrimPath.c_str(),
            UsdTokens->clipPrimPath.GetText());
        return false;
    }

    // Each Vec2d in the 'clipActive' array is a (start frame, clip index)
    // tuple. Ensure the clip index points to a valid clip.
    for (const auto& startFrameAndClipIndex : clipActive) {
        if (startFrameAndClipIndex[1] < 0 ||
            startFrameAndClipIndex[1] >= numClips) {

            *errMsg = TfStringPrintf(
                "Invalid clip index %d in metadata '%s'", 
                (int)startFrameAndClipIndex[1],
                UsdTokens->clipActive.GetText());
            return false;
        }
    }

    // Ensure that 'clipActive' does not specify multiple clips to be
    // active at the same time.
    typedef std::map<double, int> _ActiveClipMap;
    _ActiveClipMap activeClipMap;
    for (const auto& startFrameAndClipIndex : clipActive) {
        std::pair<_ActiveClipMap::iterator, bool> status = 
            activeClipMap.insert(std::make_pair(
                    startFrameAndClipIndex[0], startFrameAndClipIndex[1]));
        
        if (!status.second) {
            *errMsg = TfStringPrintf(
                "Clip %d cannot be active at time %.3f in metadata '%s' "
                "because clip %d was already specified as active at this time.",
                (int)startFrameAndClipIndex[1],
                startFrameAndClipIndex[0],
                UsdTokens->clipActive.GetText(),
                status.first->second);
            return false;
        }
    }

    return true;
}
Beispiel #7
0
/* static */
GT_DataArrayHandle
GusdPrimWrapper::convertPrimvarData( const UsdGeomPrimvar& primvar, UsdTimeCode time ) {

    SdfValueTypeName typeName = primvar.GetTypeName();
    if( typeName == SdfValueTypeNames->Int )
    {
        int usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Int32Array( &usdVal, 1, 1 );
    }
    else if( typeName == SdfValueTypeNames->Int64 )
    {
        int64_t usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Int64Array( &usdVal, 1, 1 );
    }
    else if( typeName == SdfValueTypeNames->Float )
    {
        float usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real32Array( &usdVal, 1, 1 );
    }
    else if( typeName == SdfValueTypeNames->Double )
    {
        double usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real64Array( &usdVal, 1, 1 );
    }
    else if( typeName == SdfValueTypeNames->Float3 )
    {
        GfVec3f usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real32Array( usdVal.data(), 1, 3 );
    }
    else if( typeName == SdfValueTypeNames->Double3 )
    {
        GfVec3d usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real64Array( usdVal.data(), 1, 3 );
    }
    else if( typeName == SdfValueTypeNames->Color3f )
    {
        GfVec3f usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real32Array( usdVal.data(), 1, 3, GT_TYPE_COLOR );
    }
    else if( typeName == SdfValueTypeNames->Color3d )
    {
        GfVec3d usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real64Array( usdVal.data(), 1, 3, GT_TYPE_COLOR );
    }
    else if( typeName == SdfValueTypeNames->Normal3f )
    {
        GfVec3f usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real32Array( usdVal.data(), 1, 3, GT_TYPE_NORMAL );
    }
    else if( typeName == SdfValueTypeNames->Normal3d )
    {
        GfVec3d usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real64Array( usdVal.data(), 1, 3, GT_TYPE_NORMAL );
    }
    else if( typeName == SdfValueTypeNames->Point3f )
    {
        GfVec3f usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real32Array( usdVal.data(), 1, 3, GT_TYPE_POINT );
    }
    else if( typeName == SdfValueTypeNames->Point3d )
    {
        GfVec3d usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real64Array( usdVal.data(), 1, 3, GT_TYPE_POINT );
    }
    else if( typeName == SdfValueTypeNames->Float4 )
    {
        GfVec4f usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real32Array( usdVal.data(), 1, 4 );
    }
    else if( typeName == SdfValueTypeNames->Double4 )
    {
        GfVec4d usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real64Array( usdVal.data(), 1, 4 );
    }
    else if( typeName == SdfValueTypeNames->Quatf )
    {
        GfVec4f usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real32Array( usdVal.data(), 1, 4, GT_TYPE_QUATERNION );
    }
    else if( typeName == SdfValueTypeNames->Quatd )
    {
        GfVec4d usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real64Array( usdVal.data(), 1, 4, GT_TYPE_QUATERNION );
    }
    else if( typeName == SdfValueTypeNames->Matrix3d )
    {
        GfMatrix3d usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real64Array( usdVal.GetArray(), 1, 9, GT_TYPE_MATRIX3 );
    }
    else if( typeName == SdfValueTypeNames->Matrix4d ||
             typeName == SdfValueTypeNames->Frame4d )
    {
        GfMatrix4d usdVal;
        primvar.Get( &usdVal, time );

        return new GT_Real64Array( usdVal.GetArray(), 1, 16, GT_TYPE_MATRIX );
    }
    else if( typeName == SdfValueTypeNames->String )
    {
        string usdVal;
        primvar.Get( &usdVal, time );

        auto     gtString = new GT_DAIndexedString( 1 );
        gtString->setString( 0, 0, usdVal.c_str() );
        return gtString;
    }
    else if( typeName == SdfValueTypeNames->StringArray )
    {
        VtArray<string> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        auto gtString = new GT_DAIndexedString( usdVal.size() );
        for( size_t i = 0; i < usdVal.size(); ++i )
            gtString->setString( i, 0, usdVal[i].c_str() );
        return gtString;
    }
    else if( typeName == SdfValueTypeNames->IntArray )
    {
        VtArray<int> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<int>(usdVal);
    }
    else if( typeName == SdfValueTypeNames->Int64Array )
    {
        VtArray<int64_t> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<int64_t>(usdVal);
    }
    else if( typeName == SdfValueTypeNames->FloatArray )
    {
        VtArray<float> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<float>(usdVal);
    }
    else if( typeName == SdfValueTypeNames->DoubleArray )
    {
        VtArray<double> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<double>(usdVal);
    }
    else if( typeName == SdfValueTypeNames->Float2Array )
    {
        VtArray<GfVec2f> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec2f>(usdVal);
    }
    else if( typeName == SdfValueTypeNames->Double2Array )
    {
        VtArray<GfVec2d> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec2d>(usdVal);
    }
    else if( typeName == SdfValueTypeNames->Float3Array )
    {
        VtArray<GfVec3f> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec3f>(usdVal);
    }
    else if( typeName == SdfValueTypeNames->Double3Array )
    {
        VtArray<GfVec3d> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec3d>(usdVal);
    }
    else if( typeName == SdfValueTypeNames->Color3fArray )
    {
        VtArray<GfVec3f> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec3f>(usdVal,GT_TYPE_COLOR);
    }
    else if( typeName == SdfValueTypeNames->Color3dArray )
    {
        VtArray<GfVec3d> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec3d>(usdVal,GT_TYPE_COLOR);
    }
    else if( typeName == SdfValueTypeNames->Vector3fArray )
    {
        VtArray<GfVec3f> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec3f>(usdVal, GT_TYPE_VECTOR);
    }
    else if( typeName == SdfValueTypeNames->Vector3dArray )
    {
        VtArray<GfVec3d> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec3d>(usdVal, GT_TYPE_VECTOR);
    }
    else if( typeName == SdfValueTypeNames->Normal3fArray )
    {
        VtArray<GfVec3f> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec3f>(usdVal, GT_TYPE_NORMAL);
    }
    else if( typeName == SdfValueTypeNames->Normal3dArray )
    {
        VtArray<GfVec3d> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec3d>(usdVal, GT_TYPE_NORMAL);
    }
    else if( typeName == SdfValueTypeNames->Point3fArray )
    {
        VtArray<GfVec3f> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec3f>(usdVal, GT_TYPE_POINT);
    }
    else if( typeName == SdfValueTypeNames->Point3dArray )
    {
        VtArray<GfVec3d> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec3d>(usdVal, GT_TYPE_POINT);
    }
    else if( typeName == SdfValueTypeNames->Float4Array )
    {
        VtArray<GfVec4f> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec4f>(usdVal);
    }
    else if( typeName == SdfValueTypeNames->Double4Array )
    {
        VtArray<GfVec4d> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec4d>(usdVal);
    }
    else if( typeName == SdfValueTypeNames->QuatfArray )
    {
        VtArray<GfVec4f> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec4f>(usdVal, GT_TYPE_QUATERNION);
    }
    else if( typeName == SdfValueTypeNames->QuatdArray )
    {
        VtArray<GfVec4d> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfVec4d>(usdVal, GT_TYPE_QUATERNION);
    }
    else if( typeName == SdfValueTypeNames->Matrix3dArray )
    {
        VtArray<GfMatrix3d> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfMatrix3d>(usdVal, GT_TYPE_MATRIX3);
    }
    else if( typeName == SdfValueTypeNames->Matrix4dArray ||
             typeName == SdfValueTypeNames->Frame4dArray )
    {
        VtArray<GfMatrix4d> usdVal;
        primvar.ComputeFlattened( &usdVal, time );
        return new GusdGT_VtArray<GfMatrix4d>(usdVal, GT_TYPE_MATRIX);
    }
    return NULL;
}
bool MayaMeshWriter::_addDisplayPrimvars(
        UsdGeomGprim &primSchema,
        const MFnMesh::MColorRepresentation colorRep,
        const VtArray<GfVec3f>& RGBData,
        const VtArray<float>& AlphaData,
        const TfToken& interpolation,
        const VtArray<int>& assignmentIndices,
        const int unassignedValueIndex,
        const bool clamped,
        const bool authored)
{
    // If we already have an authored value, don't try to write a new one.
    UsdAttribute colorAttr = primSchema.GetDisplayColorAttr();
    if (!colorAttr.HasAuthoredValueOpinion() && !RGBData.empty()) {
        UsdGeomPrimvar displayColor = primSchema.CreateDisplayColorPrimvar();
        if (interpolation != displayColor.GetInterpolation()) {
            displayColor.SetInterpolation(interpolation);
        }
        displayColor.Set(RGBData);
        if (!assignmentIndices.empty()) {
            displayColor.SetIndices(assignmentIndices);
            if (unassignedValueIndex != displayColor.GetUnauthoredValuesIndex()) {
               displayColor.SetUnauthoredValuesIndex(unassignedValueIndex);
            }
        }
        bool authRGB = authored;
        if (colorRep == MFnMesh::kAlpha) {
            authRGB = false;
        }
        if (authRGB) {
            if (clamped) {
                PxrUsdMayaRoundTripUtil::MarkPrimvarAsClamped(displayColor);
            }
        }
        else {
            PxrUsdMayaRoundTripUtil::MarkAttributeAsMayaGenerated(colorAttr);
        }
    }

    UsdAttribute alphaAttr = primSchema.GetDisplayOpacityAttr();
    if (!alphaAttr.HasAuthoredValueOpinion() && !AlphaData.empty()) {
        // we consider a single alpha value that is 1.0 to be the "default"
        // value.  We only want to write values that are not the "default".
        bool hasDefaultAlpha = AlphaData.size() == 1 && GfIsClose(AlphaData[0], 1.0, 1e-9);
        if (!hasDefaultAlpha) {
            UsdGeomPrimvar displayOpacity = primSchema.CreateDisplayOpacityPrimvar();
            if (interpolation != displayOpacity.GetInterpolation()) {
                displayOpacity.SetInterpolation(interpolation);
            }
            displayOpacity.Set(AlphaData);
            if (!assignmentIndices.empty()) {
                displayOpacity.SetIndices(assignmentIndices);
                if (unassignedValueIndex != displayOpacity.GetUnauthoredValuesIndex()) {
                   displayOpacity.SetUnauthoredValuesIndex(unassignedValueIndex);
                }
            }
            bool authAlpha = authored;
            if (colorRep == MFnMesh::kRGB) {
                authAlpha = false;
            }
            if (authAlpha) {
                if (clamped) {
                    PxrUsdMayaRoundTripUtil::MarkPrimvarAsClamped(displayOpacity);
                }
            }
            else {
                PxrUsdMayaRoundTripUtil::MarkAttributeAsMayaGenerated(alphaAttr);
            }
        }
    }

    return true;
}
/// Collect values from the color set named \p colorSet.
/// If \p isDisplayColor is true and this color set represents displayColor,
/// the unauthored/unpainted values in the color set will be filled in using
/// the shader values in \p shadersRGBData and \p shadersAlphaData if available.
/// Values are gathered per face vertex, but then the data is compressed to
/// vertex, uniform, or constant interpolation if possible.
/// Unauthored/unpainted values will be given the index -1.
bool MayaMeshWriter::_GetMeshColorSetData(
        MFnMesh& mesh,
        const MString& colorSet,
        bool isDisplayColor,
        const VtArray<GfVec3f>& shadersRGBData,
        const VtArray<float>& shadersAlphaData,
        const VtArray<int>& shadersAssignmentIndices,
        VtArray<GfVec3f>* colorSetRGBData,
        VtArray<float>* colorSetAlphaData,
        TfToken* interpolation,
        VtArray<int>* colorSetAssignmentIndices,
        MFnMesh::MColorRepresentation* colorSetRep,
        bool* clamped)
{
    // If there are no colors, return immediately as failure.
    if (mesh.numColors(colorSet) == 0) {
        return false;
    }

    MColorArray colorSetData;
    const MColor unsetColor(-FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX);
    if (mesh.getFaceVertexColors(colorSetData, &colorSet, &unsetColor)
            == MS::kFailure) {
        return false;
    }

    if (colorSetData.length() == 0) {
        return false;
    }

    // Get the color set representation and clamping.
    *colorSetRep = mesh.getColorRepresentation(colorSet);
    *clamped = mesh.isColorClamped(colorSet);

    // We'll populate the assignment indices for every face vertex, but we'll
    // only push values into the data if the face vertex has a value. All face
    // vertices are initially unassigned/unauthored.
    colorSetRGBData->clear();
    colorSetAlphaData->clear();
    colorSetAssignmentIndices->assign((size_t)colorSetData.length(), -1);
    *interpolation = UsdGeomTokens->faceVarying;

    // Loop over every face vertex to populate the value arrays.
    MItMeshFaceVertex itFV(mesh.object());
    unsigned int fvi = 0;
    for (itFV.reset(); !itFV.isDone(); itFV.next(), ++fvi) {
        // If this is a displayColor color set, we may need to fallback on the
        // bound shader colors/alphas for this face in some cases. In
        // particular, if the color set is alpha-only, we fallback on the
        // shader values for the color. If the color set is RGB-only, we
        // fallback on the shader values for alpha only. If there's no authored
        // color for this face vertex, we use both the color AND alpha values
        // from the shader.
        bool useShaderColorFallback = false;
        bool useShaderAlphaFallback = false;
        if (isDisplayColor) {
            if (colorSetData[fvi] == unsetColor) {
                useShaderColorFallback = true;
                useShaderAlphaFallback = true;
            } else if (*colorSetRep == MFnMesh::kAlpha) {
                // The color set does not provide color, so fallback on shaders.
                useShaderColorFallback = true;
            } else if (*colorSetRep == MFnMesh::kRGB) {
                // The color set does not provide alpha, so fallback on shaders.
                useShaderAlphaFallback = true;
            }
        }

        // If we're exporting displayColor and we use the value from the color
        // set, we need to convert it to linear.
        bool convertDisplayColorToLinear = isDisplayColor;

        // Shader values for the mesh could be constant
        // (shadersAssignmentIndices is empty) or uniform.
        int faceIndex = itFV.faceId();
        if (useShaderColorFallback) {
            // There was no color value in the color set to use, so we use the
            // shader color, or the default color if there is no shader color.
            // This color will already be in linear space, so don't convert it
            // again.
            convertDisplayColorToLinear = false;

            int valueIndex = -1;
            if (shadersAssignmentIndices.empty()) {
                if (shadersRGBData.size() == 1) {
                    valueIndex = 0;
                }
            } else if (faceIndex >= 0 && 
                static_cast<size_t>(faceIndex) < shadersAssignmentIndices.size()) {

                int tmpIndex = shadersAssignmentIndices[faceIndex];
                if (tmpIndex >= 0 && 
                    static_cast<size_t>(tmpIndex) < shadersRGBData.size()) {
                    valueIndex = tmpIndex;
                }
            }
            if (valueIndex >= 0) {
                colorSetData[fvi][0] = shadersRGBData[valueIndex][0];
                colorSetData[fvi][1] = shadersRGBData[valueIndex][1];
                colorSetData[fvi][2] = shadersRGBData[valueIndex][2];
            } else {
                // No shader color to fallback on. Use the default shader color.
                colorSetData[fvi][0] = _ShaderDefaultRGB[0];
                colorSetData[fvi][1] = _ShaderDefaultRGB[1];
                colorSetData[fvi][2] = _ShaderDefaultRGB[2];
            }
        }
        if (useShaderAlphaFallback) {
            int valueIndex = -1;
            if (shadersAssignmentIndices.empty()) {
                if (shadersAlphaData.size() == 1) {
                    valueIndex = 0;
                }
            } else if (faceIndex >= 0 && 
                static_cast<size_t>(faceIndex) < shadersAssignmentIndices.size()) {
                int tmpIndex = shadersAssignmentIndices[faceIndex];
                if (tmpIndex >= 0 && 
                    static_cast<size_t>(tmpIndex) < shadersAlphaData.size()) {
                    valueIndex = tmpIndex;
                }
            }
            if (valueIndex >= 0) {
                colorSetData[fvi][3] = shadersAlphaData[valueIndex];
            } else {
                // No shader alpha to fallback on. Use the default shader alpha.
                colorSetData[fvi][3] = _ShaderDefaultAlpha;
            }
        }

        // If we have a color/alpha value, add it to the data to be returned.
        if (colorSetData[fvi] != unsetColor) {
            GfVec3f rgbValue = _ColorSetDefaultRGB;
            float alphaValue = _ColorSetDefaultAlpha;

            if (useShaderColorFallback              || 
                    (*colorSetRep == MFnMesh::kRGB) || 
                    (*colorSetRep == MFnMesh::kRGBA)) {
                rgbValue = _LinearColorFromColorSet(colorSetData[fvi],
                                                    convertDisplayColorToLinear);
            }
            if (useShaderAlphaFallback                || 
                    (*colorSetRep == MFnMesh::kAlpha) || 
                    (*colorSetRep == MFnMesh::kRGBA)) {
                alphaValue = colorSetData[fvi][3];
            }

            colorSetRGBData->push_back(rgbValue);
            colorSetAlphaData->push_back(alphaValue);
            (*colorSetAssignmentIndices)[fvi] = colorSetRGBData->size() - 1;
        }
    }

    _MergeEquivalentColorSetValues(colorSetRGBData,
                                   colorSetAlphaData,
                                   colorSetAssignmentIndices);
    PxrUsdMayaUtil::CompressFaceVaryingPrimvarIndices(mesh,
                                                      interpolation,
                                                      colorSetAssignmentIndices);

    return true;
}
Beispiel #10
0
static void testArray() {

    VtDoubleArray da(60);

    double val = 1;
    TF_FOR_ALL(elem, da)
        *elem = val++;

    val = 1;
    for (VtDoubleArray::const_iterator i = da.begin(); i != da.end(); ++i)
        if (*i != val++)
            die("iterator");

    // Do copy-on-write cases.
    VtDoubleArray da2 = da;
    da2[0] = 333.333;

    if (da2[0] != 333.333 || 
        da[0] == 333.333)
        die("copy-on-write");

    // Try swapping
    VtDoubleArray daCopy = da;
    VtDoubleArray da2Copy = da2;

    da.swap(da2);
    TF_AXIOM(da == da2Copy);
    TF_AXIOM(da2 == daCopy);

    using std::swap;
    swap(da, da2);
    TF_AXIOM(da == daCopy);
    TF_AXIOM(da2 == da2Copy);

    {
        // Try default-constructing a VtArray.
        VtDoubleArray def;
        TF_AXIOM(def.size() == 0);
        
        // Try iterating over the array.
        std::vector<double> v(def.begin(), def.end());
        TF_AXIOM(v.empty());

        // Test resizing a default constructed array.
        def.resize(123);
        TF_AXIOM(def.size() == 123);
    }

    {
        // Try creating an empty VtArray.
        VtDoubleArray array(0);
        TF_AXIOM(array.size() == 0);

        // Try iterating over the array.
        std::vector<double> v(array.begin(), array.end());
        TF_AXIOM(v.empty());
    }

    {
        // Array push_back and resize.
        VtDoubleArray array(0);

        // Push back on a rank-1 array.
        TF_AXIOM(array.size() == 0);
        array.push_back(1.234);
        TF_AXIOM(array.size() == 1);
        TF_AXIOM(array[0] == 1.234);

        array.push_back(2.3456);
        TF_AXIOM(array.size() == 2);
        TF_AXIOM(array[0] == 1.234);
        TF_AXIOM(array[1] == 2.3456);

        array.pop_back();
        TF_AXIOM(array.size() == 1);
        TF_AXIOM(array[0] == 1.234);

        // Resize should preserve elements.
        array.resize(100);
        TF_AXIOM(array.size() == 100);
        TF_AXIOM(array[0] == 1.234);
        TF_AXIOM(array[1] == 0.0);
        TF_AXIOM(array[50] == 0.0);
        TF_AXIOM(array[99] == 0.0);

        for (size_t i = 0; i != 100; ++i)
            array[i] = i;

        array.resize(1000);
        TF_AXIOM(array.size() == 1000);
        for (size_t i = 0; i != 1000; ++i) {
            if (i < 100) {
                TF_AXIOM(array[i] == i);
            } else {
                TF_AXIOM(array[i] == 0);
            }
        }

        array.resize(10);
        TF_AXIOM(array.size() == 10);
        for (size_t i = 0; i != 10; ++i) {
            TF_AXIOM(array[i] == i);
        }

        array.pop_back();
        array.pop_back();
        array.pop_back();
        array.pop_back();
        array.pop_back();

        TF_AXIOM(array.size() == 5);

    }

    {
        // Test that mutating shape data doesn't affect copies of an array.
        VtArray<int> a(4);
        a._GetShapeData()->otherDims[0] = 4;
        a._GetShapeData()->otherDims[1] = 0;

        VtArray<int> b = a;
        const auto &ca = a;
        const auto &cb = b;
        TF_AXIOM(ca._GetShapeData()->otherDims[0] ==
                 cb._GetShapeData()->otherDims[0]);
        TF_AXIOM(ca._GetShapeData()->otherDims[1] ==
                 cb._GetShapeData()->otherDims[1]);

        b._GetShapeData()->otherDims[0] = 2;
        b._GetShapeData()->otherDims[1] = 2;
        b._GetShapeData()->otherDims[2] = 0;

        // Check that a's shape data is unchanged
        TF_AXIOM(ca._GetShapeData()->otherDims[0] == 4);
        TF_AXIOM(ca._GetShapeData()->otherDims[1] == 0);

        // and that b's shape data has been updated as expected.
        TF_AXIOM(cb._GetShapeData()->otherDims[0] == 2);
        TF_AXIOM(cb._GetShapeData()->otherDims[1] == 2);
        TF_AXIOM(cb._GetShapeData()->otherDims[2] == 0);
    }
    
    {
        // Test initializer lists for VtArrays;
        VtArray<int> array1({1, 2, 3, 4});
        TF_AXIOM(array1.size() == 4);
        TF_AXIOM(array1[0] == 1);
        TF_AXIOM(array1[1] == 2);
        TF_AXIOM(array1[2] == 3);
        TF_AXIOM(array1[3] == 4);
        array1.assign({5, 6});
        TF_AXIOM(array1.size() == 2);
        TF_AXIOM(array1[0] == 5);
        TF_AXIOM(array1[1] == 6);
        array1.assign({});
        TF_AXIOM(array1.size() == 0);
        array1 = {7, 8, 9};
        TF_AXIOM(array1.size() == 3);
        TF_AXIOM(array1[0] == 7);
        TF_AXIOM(array1[1] == 8);
        TF_AXIOM(array1[2] == 9);
        array1 = {};
        TF_AXIOM(array1.size() == 0);
        
        VtArray<int> empty({});
        TF_AXIOM(empty.size() == 0);

        auto testImplicit = [](const VtArray<int>& array, size_t size) {
            TF_AXIOM(array.size() == size);
        };
        testImplicit({1,2,3}, 3);
    }

    {
        // Test VtArray -> TfSpan conversions.

        const VtIntArray constData({1,2,3,4,5});
        
        {
            VtIntArray copy(constData);

            TfSpan<const int> span = copy;
            // Make sure we didn't detach.
            TF_AXIOM(span.data() == constData.cdata());
            TF_AXIOM(span.size() == static_cast<std::ptrdiff_t>(copy.size()));
        }
        {
            VtIntArray copy(constData);

            auto span = TfMakeConstSpan(copy);
            // Make sure we didn't detach.
            TF_AXIOM(span.data() == constData.cdata());
            TF_AXIOM(span.size() == static_cast<std::ptrdiff_t>(copy.size()));
        }

        {
            VtIntArray copy(constData);

            TfSpan<int> span = copy;
            // Should have detached.
            TF_AXIOM(span.data() == copy.cdata() &&
                     span.data() != constData.cdata());
            TF_AXIOM(span.size() == static_cast<std::ptrdiff_t>(copy.size()));
        }

        {
            VtIntArray copy(constData);
            
            auto span = TfMakeSpan(copy);
            // Should have detached.
            TF_AXIOM(span.data() == copy.cdata() &&
                     span.data() != constData.cdata());
            TF_AXIOM(span.size() == static_cast<std::ptrdiff_t>(copy.size()));
        }
    }
}
Beispiel #11
0
/* static */
bool
PxrUsdMayaTranslatorCurves::Create(
        const UsdGeomCurves& curves,
        MObject parentNode,
        const PxrUsdMayaPrimReaderArgs& args,
        PxrUsdMayaPrimReaderContext* context)
{
    if (not curves) {
        return false;
    }

    const UsdPrim& prim = curves.GetPrim();

    MStatus status;

    // Create node (transform)
    MObject mayaNodeTransformObj;
    if (not PxrUsdMayaTranslatorUtil::CreateTransformNode(prim,
                                                          parentNode,
                                                          args,
                                                          context,
                                                          &status,
                                                          &mayaNodeTransformObj)) {
        return false;
    }

    VtArray<GfVec3f> points;
    VtArray<int>     curveOrder;
    VtArray<int>     curveVertexCounts;
    VtArray<float>   curveWidths;
    VtArray<GfVec2d> curveRanges;
    VtArray<double>  curveKnots;

    // LIMITATION:  xxx REVISIT xxx
    //   Non-animated Attrs
    //   Assuming that a number of these USD attributes are assumed to not be animated
    //   Some we may want to expose as animatable later.
    //
    curves.GetCurveVertexCountsAttr().Get(&curveVertexCounts); // not animatable

    // XXX:
    // Only supporting single curve for now.
    // Sanity Checks
    if (curveVertexCounts.size() == 0) {
        MGlobal::displayError(
            TfStringPrintf("VertexCount arrays is empty on NURBS curves <%s>. Skipping...", 
                            prim.GetPath().GetText()).c_str());
        return false; // No verts for the curve, so exit
    } else if (curveVertexCounts.size() > 1) {
        MGlobal::displayWarning(
            TfStringPrintf("Multiple curves in <%s>. Reading first one...", 
                            prim.GetPath().GetText()).c_str());
    }

    int curveIndex = 0;
    curves.GetWidthsAttr().Get(&curveWidths); // not animatable

    // Gather points. If args.GetReadAnimData() is TRUE,
    // pick the first avaiable sample or default
    UsdTimeCode pointsTimeSample=UsdTimeCode::EarliestTime();
    std::vector<double> pointsTimeSamples;
    size_t numTimeSamples = 0;
    if (args.GetReadAnimData()) {
        curves.GetPointsAttr().GetTimeSamples(&pointsTimeSamples);
        numTimeSamples = pointsTimeSamples.size();
        if (numTimeSamples>0) {
            pointsTimeSample = pointsTimeSamples[0];
        }
    }
    curves.GetPointsAttr().Get(&points, pointsTimeSample);
    
    if (points.size() == 0) {
        MGlobal::displayError(
            TfStringPrintf("Points arrays is empty on NURBS curves <%s>. Skipping...", 
                            prim.GetPath().GetText()).c_str());
        return false; // invalid nurbscurves, so exit
    }

    if (UsdGeomNurbsCurves nurbsSchema = UsdGeomNurbsCurves(prim)) {
        nurbsSchema.GetOrderAttr().Get(&curveOrder);   // not animatable
        nurbsSchema.GetKnotsAttr().Get(&curveKnots);   // not animatable
        nurbsSchema.GetRangesAttr().Get(&curveRanges); // not animatable
    } else {

        // Handle basis curves originally modelled in Maya as nurbs.

        curveOrder.resize(1);
        UsdGeomBasisCurves basisSchema = UsdGeomBasisCurves(prim);
        TfToken typeToken;
        basisSchema.GetTypeAttr().Get(&typeToken);
        if (typeToken == UsdGeomTokens->linear) {
            curveOrder[0] = 2;
            curveKnots.resize(points.size());
            for (size_t i=0; i < curveKnots.size(); ++i) {
                curveKnots[i] = i;
            }
        } else {
            curveOrder[0] = 4;

            // Strip off extra end points; assuming this is non-periodic.
            VtArray<GfVec3f> tmpPts(points.size() - 2);
            std::copy(points.begin() + 1, points.end() - 1, tmpPts.begin());
            points.swap(tmpPts);

            // Cubic curves in Maya have numSpans + 2*3 - 1, and for geometry
            // that came in as basis curves, we have numCV's - 3 spans.  See the
            // MFnNurbsCurve documentation and the nurbs curve export
            // implementation in mojitoplugmaya for more details.
            curveKnots.resize(points.size() -3 + 5);
            int knotIdx = 0;
            for (size_t i=0; i < curveKnots.size(); ++i) {
                if (i < 3) {
                    curveKnots[i] = 0.0;
                } else {
                    if (i <= curveKnots.size() - 3) {
                        ++knotIdx;
                    }
                    curveKnots[i] = double(knotIdx);
                } 
            }
        }
    }

    // == Convert data
    size_t mayaNumVertices = points.size();
    MPointArray mayaPoints(mayaNumVertices);
    for (size_t i=0; i < mayaNumVertices; i++) {
        mayaPoints.set( i, points[i][0], points[i][1], points[i][2] );
    }

    double *knots=curveKnots.data();
    MDoubleArray mayaKnots( knots, curveKnots.size());

    int mayaDegree = curveOrder[curveIndex] - 1;

    MFnNurbsCurve::Form mayaCurveForm = MFnNurbsCurve::kOpen; // HARDCODED
    bool mayaCurveCreate2D = false;
    bool mayaCurveCreateRational = true;

    // == Create NurbsCurve Shape Node
    MFnNurbsCurve curveFn;
    MObject curveObj = curveFn.create(mayaPoints, 
                                     mayaKnots,
                                     mayaDegree,
                                     mayaCurveForm,
                                     mayaCurveCreate2D,
                                     mayaCurveCreateRational,
                                     mayaNodeTransformObj,
                                     &status
                                     );
     if (status != MS::kSuccess) {
         return false;
     }
    MString nodeName( prim.GetName().GetText() );
    nodeName += "Shape";
    curveFn.setName(nodeName, false, &status);

    std::string nodePath( prim.GetPath().GetText() );
    nodePath += "/";
    nodePath += nodeName.asChar();
    if (context) {
        context->RegisterNewMayaNode( nodePath, curveObj ); // used for undo/redo
    }

    // == Animate points ==
    //   Use blendShapeDeformer so that all the points for a frame are contained in a single node
    //   Almost identical code as used with MayaMeshReader.cpp
    //
    if (numTimeSamples > 0) {
        MPointArray mayaPoints(mayaNumVertices);
        MObject curveAnimObj;

        MFnBlendShapeDeformer blendFn;
        MObject blendObj = blendFn.create(curveObj);
        if (context) {
            context->RegisterNewMayaNode(blendFn.name().asChar(), blendObj ); // used for undo/redo
        }
        
        for (unsigned int ti=0; ti < numTimeSamples; ++ti) {
             curves.GetPointsAttr().Get(&points, pointsTimeSamples[ti]);

            for (unsigned int i=0; i < mayaNumVertices; i++) {
                mayaPoints.set( i, points[i][0], points[i][1], points[i][2] );
            }

            // == Create NurbsCurve Shape Node
            MFnNurbsCurve curveFn;
            if ( curveAnimObj.isNull() ) {
                curveAnimObj = curveFn.create(mayaPoints, 
                                     mayaKnots,
                                     mayaDegree,
                                     mayaCurveForm,
                                     mayaCurveCreate2D,
                                     mayaCurveCreateRational,
                                     mayaNodeTransformObj,
                                     &status
                                     );
                if (status != MS::kSuccess) {
                    continue;
                }
            }
            else {
                // Reuse the already created curve by copying it and then setting the points
                curveAnimObj = curveFn.copy(curveAnimObj, mayaNodeTransformObj, &status);
                curveFn.setCVs(mayaPoints);
            }
            blendFn.addTarget(curveObj, ti, curveAnimObj, 1.0);
            curveFn.setIntermediateObject(true);
            if (context) {
                context->RegisterNewMayaNode( curveFn.fullPathName().asChar(), curveAnimObj ); // used for undo/redo
            }
        }

        // Animate the weights so that curve0 has a weight of 1 at frame 0, etc.
        MFnAnimCurve animFn;

        // Construct the time array to be used for all the keys
        MTimeArray timeArray;
        timeArray.setLength(numTimeSamples);
        for (unsigned int ti=0; ti < numTimeSamples; ++ti) {
            timeArray.set( MTime(pointsTimeSamples[ti]), ti);
        }

        // Key/Animate the weights
        MPlug plgAry = blendFn.findPlug( "weight" );
        if ( !plgAry.isNull() && plgAry.isArray() ) {
            for (unsigned int ti=0; ti < numTimeSamples; ++ti) {
                MPlug plg = plgAry.elementByLogicalIndex(ti, &status);
                MDoubleArray valueArray(numTimeSamples, 0.0);
                valueArray[ti] = 1.0; // Set the time value where this curve's weight should be 1.0
                MObject animObj = animFn.create(plg, NULL, &status);
                animFn.addKeys(&timeArray, &valueArray);
                if (context) {
                    context->RegisterNewMayaNode(animFn.name().asChar(), animObj ); // used for undo/redo
                }
            }
        }
    }

    return true;
}
void
UsdMayaPrimReaderSkelRoot::PostReadSubtree(
    UsdMayaPrimReaderContext* context)
{
    UsdSkelRoot skelRoot(_GetArgs().GetUsdPrim());
    if (!TF_VERIFY(skelRoot))
        return;
    
    // Compute skel bindings and create skin clusters for bound skels
    // We do this in a post-subtree stage to ensure that any skinnable
    // prims we produce skin clusters for have been processed first.

    _cache.Populate(skelRoot);

    std::vector<UsdSkelBinding> bindings;
    if (!_cache.ComputeSkelBindings(skelRoot, &bindings)) {
        return;
    }

    for (const UsdSkelBinding& binding : bindings) {
        if (binding.GetSkinningTargets().empty())
            continue;

        if (const UsdSkelSkeletonQuery& skelQuery =
            _cache.GetSkelQuery(binding.GetSkeleton())) {
            
            VtArray<MObject> joints;
            if (!UsdMayaTranslatorSkel::GetJoints(
                    skelQuery, context, &joints)) {
                continue;
            }
            
            for (const auto& skinningQuery : binding.GetSkinningTargets()) {

                const UsdPrim& skinnedPrim = skinningQuery.GetPrim();

                // Get an ordering of the joints that matches the ordering of
                // the binding.
                VtArray<MObject> skinningJoints;
                if (!skinningQuery.GetMapper()) {
                    skinningJoints = joints;
                } else {
                    // TODO:
                    // UsdSkelAnimMapper currently only supports remapping
                    // of Sdf value types, so we can't easily use it here.
                    // For now, we can delegate remapping behavior by
                    // remapping ordered joint indices.
                    VtIntArray indices(joints.size());
                    for (size_t i = 0; i < joints.size(); ++i)
                        indices[i] = i;
                    
                    VtIntArray remappedIndices;
                    if (!skinningQuery.GetMapper()->Remap(
                            indices, &remappedIndices)) {
                        continue;
                    }
                    
                    skinningJoints.resize(remappedIndices.size());
                    for (size_t i = 0; i < remappedIndices.size(); ++i) {
                        int index = remappedIndices[i];
                        if (index >= 0 && static_cast<size_t>(index) < joints.size()) {
                            skinningJoints[i] = joints[index];
                        }
                    }
                }

                MObject bindPose
                    = UsdMayaTranslatorSkel::GetBindPose(skelQuery, context);

                // Add a skin cluster to skin this prim.
                UsdMayaTranslatorSkel::CreateSkinCluster(
                    skelQuery, skinningQuery, skinningJoints,
                    skinnedPrim, _GetArgs(), context, bindPose);
            }
        }
    }
}