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; }
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; }
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; }
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; }
/* 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; }
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())); } } }
/* 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); } } } }