static MStatus convertToMayaMeshData(OpenSubdiv::Far::TopologyRefiner const & refiner, std::vector<Vertex> const & refinedVerts, bool hasUVs, std::vector<FVarVertexUV> const & refinedUVs, bool hasColors, std::vector<FVarVertexColor> const & refinedColors, MFnMesh & inMeshFn, MObject newMeshDataObj) { MStatus status; typedef OpenSubdiv::Far::ConstIndexArray IndexArray; int maxlevel = refiner.GetMaxLevel(); OpenSubdiv::Far::TopologyLevel const & refLastLevel = refiner.GetLevel(maxlevel); int nfaces = refLastLevel.GetNumFaces(); // Init Maya Data // Face Counts MIntArray faceCounts(nfaces); for (int face=0; face < nfaces; ++face) { faceCounts[face] = 4; } // Face Connects MIntArray faceConnects(nfaces*4); for (int face=0, idx=0; face < nfaces; ++face) { IndexArray fverts = refLastLevel.GetFaceVertices(face); for (int vert=0; vert < fverts.size(); ++vert) { faceConnects[idx++] = fverts[vert]; } } // Points int nverts = refLastLevel.GetNumVertices(); int firstOfLastVert = refiner.GetNumVerticesTotal() - nverts - refiner.GetLevel(0).GetNumVertices(); MFloatPointArray points(nverts); for (int vIt = 0; vIt < nverts; ++vIt) { Vertex const & v = refinedVerts[firstOfLastVert + vIt]; points.set(vIt, v.position[0], v.position[1], v.position[2]); } // Create New Mesh from MFnMesh MFnMesh newMeshFn; MObject newMeshObj = newMeshFn.create(points.length(), faceCounts.length(), points, faceCounts, faceConnects, newMeshDataObj, &status); MCHECKERR(status, "Cannot create new mesh"); // Get face-varying set names and other info from the inMesh MStringArray uvSetNames; MStringArray colorSetNames; std::vector<int> colorSetChannels; std::vector<MFnMesh::MColorRepresentation> colorSetReps; int totalColorSetChannels = 0; status = getMayaFvarFieldParams(inMeshFn, uvSetNames, colorSetNames, colorSetChannels, colorSetReps, totalColorSetChannels); // Add new UVs back to the mesh if needed if (hasUVs) { MIntArray fvarConnects(faceConnects.length()); int count = 0; for (int f = 0; f < refLastLevel.GetNumFaces(); ++f) { IndexArray faceIndices = refLastLevel.GetFaceFVarValues(f, CHANNELUV); for (int index = 0 ; index < faceIndices.size() ; ++index) { fvarConnects[count++] = faceIndices[index]; } } int nuvs = refLastLevel.GetNumFVarValues(CHANNELUV); int firstOfLastUvs = refiner.GetNumFVarValuesTotal(CHANNELUV) - nuvs - refiner.GetLevel(0).GetNumFVarValues(CHANNELUV); MFloatArray uCoord(nuvs), vCoord(nuvs); for (int uvIt = 0; uvIt < nuvs; ++uvIt) { FVarVertexUV const & uv = refinedUVs[firstOfLastUvs + uvIt]; uCoord[uvIt] = uv.u; vCoord[uvIt] = uv.v; } // Currently, the plugin only supports one UV set int uvSetIndex = 0; if (uvSetIndex > 0) { status = newMeshFn.createUVSetDataMesh( uvSetNames[uvSetIndex] ); MCHECKERR(status, "Cannot create UVSet"); } static MString defaultUVName("map1"); MString const * uvname = uvSetIndex==0 ? &defaultUVName : &uvSetNames[uvSetIndex]; status = newMeshFn.setUVs(uCoord, vCoord, uvname); MCHECKERR(status, "Cannot set UVs for set : "+*uvname); status = newMeshFn.assignUVs(faceCounts, fvarConnects, uvname); MCHECKERR(status, "Cannot assign UVs"); } // Add new colors back to the mesh if needed if (hasColors) { int count = 0; MIntArray fvarConnects2(faceConnects.length()); for (int f = 0 ; f < refLastLevel.GetNumFaces(); ++f) { IndexArray faceIndices = refLastLevel.GetFaceFVarValues(f, CHANNELCOLOR); for (int index = 0 ; index < faceIndices.size() ; ++index) { fvarConnects2[count++] = faceIndices[index]; } } int ncols = refLastLevel.GetNumFVarValues(CHANNELCOLOR); int firstOfLastCols = refiner.GetNumFVarValuesTotal(CHANNELCOLOR) - ncols - refiner.GetLevel(0).GetNumFVarValues(CHANNELCOLOR); MColorArray colorArray(ncols); for (int colIt = 0; colIt < ncols; ++colIt) { FVarVertexColor const & c = refinedColors[firstOfLastCols + colIt]; colorArray.set(colIt, c.r, c.g, c.b, c.a); } // Currently, the plugin only supports one color sets int colorSetIndex = 0; // Assign color buffer and map the ids for each face-vertex // API Limitation: Cannot set MColorRepresentation here status = newMeshFn.createColorSetDataMesh( colorSetNames[colorSetIndex]); MCHECKERR(status, "Cannot create ColorSet"); bool isColorClamped = inMeshFn.isColorClamped( colorSetNames[colorSetIndex], &status); MCHECKERR(status, "Can not get Color Clamped "); status = newMeshFn.setIsColorClamped( colorSetNames[colorSetIndex], isColorClamped); MCHECKERR(status, "Can not set Color Clamped : " + isColorClamped); status = newMeshFn.setColors( colorArray, &colorSetNames[colorSetIndex], colorSetReps[colorSetIndex]); MCHECKERR(status, "Can not set Colors"); status = newMeshFn.assignColors( fvarConnects2, &colorSetNames[colorSetIndex]); MCHECKERR(status, "Can not assign Colors"); } return MS::kSuccess; }
/* static */ bool UsdMayaTranslatorMesh::_AssignColorSetPrimvarToMesh( const UsdGeomMesh& primSchema, const UsdGeomPrimvar& primvar, MFnMesh& meshFn) { const TfToken& primvarName = primvar.GetPrimvarName(); const SdfValueTypeName& typeName = primvar.GetTypeName(); MString colorSetName(primvarName.GetText()); // If the primvar is displayOpacity and it is a FloatArray, check if // displayColor is authored. If not, we'll import this 'displayOpacity' // primvar as a 'displayColor' color set. This supports cases where the // user created a single channel value for displayColor. // Note that if BOTH displayColor and displayOpacity are authored, they will // be imported as separate color sets. We do not attempt to combine them // into a single color set. if (primvarName == UsdMayaMeshColorSetTokens->DisplayOpacityColorSetName && typeName == SdfValueTypeNames->FloatArray) { if (!UsdMayaRoundTripUtil::IsAttributeUserAuthored(primSchema.GetDisplayColorPrimvar())) { colorSetName = UsdMayaMeshColorSetTokens->DisplayColorColorSetName.GetText(); } } // We'll need to convert colors from linear to display if this color set is // for display colors. const bool isDisplayColor = (colorSetName == UsdMayaMeshColorSetTokens->DisplayColorColorSetName.GetText()); // Get the raw data before applying any indexing. We'll only populate one // of these arrays based on the primvar's typeName, and we'll also set the // color representation so we know which array to use later. VtFloatArray alphaArray; VtVec3fArray rgbArray; VtVec4fArray rgbaArray; MFnMesh::MColorRepresentation colorRep; size_t numValues = 0; MStatus status = MS::kSuccess; if (typeName == SdfValueTypeNames->FloatArray) { colorRep = MFnMesh::kAlpha; if (!primvar.Get(&alphaArray) || alphaArray.empty()) { status = MS::kFailure; } else { numValues = alphaArray.size(); } } else if (typeName == SdfValueTypeNames->Float3Array || typeName == SdfValueTypeNames->Color3fArray) { colorRep = MFnMesh::kRGB; if (!primvar.Get(&rgbArray) || rgbArray.empty()) { status = MS::kFailure; } else { numValues = rgbArray.size(); } } else if (typeName == SdfValueTypeNames->Float4Array || typeName == SdfValueTypeNames->Color4fArray) { colorRep = MFnMesh::kRGBA; if (!primvar.Get(&rgbaArray) || rgbaArray.empty()) { status = MS::kFailure; } else { numValues = rgbaArray.size(); } } else { TF_WARN("Unsupported color set primvar type '%s' for primvar '%s' on " "mesh: %s", typeName.GetAsToken().GetText(), primvarName.GetText(), primvar.GetAttr().GetPrimPath().GetText()); return false; } if (status != MS::kSuccess || numValues == 0) { TF_WARN("Could not read color set values from primvar '%s' on mesh: %s", primvarName.GetText(), primvar.GetAttr().GetPrimPath().GetText()); return false; } VtIntArray assignmentIndices; int unauthoredValuesIndex = -1; if (primvar.GetIndices(&assignmentIndices)) { // The primvar IS indexed, so the indices array is what determines the // number of color values. numValues = assignmentIndices.size(); unauthoredValuesIndex = primvar.GetUnauthoredValuesIndex(); } // Go through the color data and translate the values into MColors in the // colorArray, taking into consideration that indexed data may have been // authored sparsely. If the assignmentIndices array is empty then the data // is NOT indexed. // Note that with indexed data, the data is added to the arrays in ascending // component ID order according to the primvar's interpolation (ascending // face ID for uniform interpolation, ascending vertex ID for vertex // interpolation, etc.). This ordering may be different from the way the // values are ordered in the primvar. Because of this, we recycle the // assignmentIndices array as we go to store the new mapping from component // index to color index. MColorArray colorArray; for (size_t i = 0; i < numValues; ++i) { int valueIndex = i; if (i < assignmentIndices.size()) { // The data is indexed, so consult the indices array for the // correct index into the data. valueIndex = assignmentIndices[i]; if (valueIndex == unauthoredValuesIndex) { // This component is unauthored, so just update the // mapping in assignmentIndices and then skip the value. // We don't actually use the value at the unassigned index. assignmentIndices[i] = -1; continue; } // We'll be appending a new value, so the current length of the // array gives us the new value's index. assignmentIndices[i] = colorArray.length(); } GfVec4f colorValue(1.0); switch(colorRep) { case MFnMesh::kAlpha: colorValue[3] = alphaArray[valueIndex]; break; case MFnMesh::kRGB: colorValue[0] = rgbArray[valueIndex][0]; colorValue[1] = rgbArray[valueIndex][1]; colorValue[2] = rgbArray[valueIndex][2]; break; case MFnMesh::kRGBA: colorValue[0] = rgbaArray[valueIndex][0]; colorValue[1] = rgbaArray[valueIndex][1]; colorValue[2] = rgbaArray[valueIndex][2]; colorValue[3] = rgbaArray[valueIndex][3]; break; default: break; } if (isDisplayColor) { colorValue = UsdMayaColorSpace::ConvertLinearToMaya(colorValue); } MColor mColor(colorValue[0], colorValue[1], colorValue[2], colorValue[3]); colorArray.append(mColor); } // colorArray now stores all of the values and any unassigned components // have had their indices set to -1, so update the unauthored values index. unauthoredValuesIndex = -1; const bool clamped = UsdMayaRoundTripUtil::IsPrimvarClamped(primvar); status = meshFn.createColorSet(colorSetName, nullptr, clamped, colorRep); if (status != MS::kSuccess) { TF_WARN("Unable to create color set '%s' for mesh: %s", colorSetName.asChar(), meshFn.fullPathName().asChar()); return false; } // Create colors on the mesh from the values we collected out of the // primvar. We'll assign mesh components to these values below. status = meshFn.setColors(colorArray, &colorSetName, colorRep); if (status != MS::kSuccess) { TF_WARN("Unable to set color data on color set '%s' for mesh: %s", colorSetName.asChar(), meshFn.fullPathName().asChar()); return false; } const TfToken& interpolation = primvar.GetInterpolation(); // Build an array of value assignments for each face vertex in the mesh. // Any assignments left as -1 will not be assigned a value. MIntArray colorIds = _GetMayaFaceVertexAssignmentIds(meshFn, interpolation, assignmentIndices, unauthoredValuesIndex); status = meshFn.assignColors(colorIds, &colorSetName); if (status != MS::kSuccess) { TF_WARN("Could not assign color values to color set '%s' on mesh: %s", colorSetName.asChar(), meshFn.fullPathName().asChar()); return false; } return true; }
MStatus convertOsdFarToMayaMeshData( FMesh const * farMesh, OpenSubdiv::OsdCpuVertexBuffer * vertexBuffer, int subdivisionLevel, MFnMesh const & inMeshFn, MObject newMeshDataObj ) { MStatus returnStatus; // Get sizing data from OSD const OpenSubdiv::FarPatchTables *farPatchTables = farMesh->GetPatchTables(); int numPolygons = farPatchTables->GetNumFaces(); // use the highest level stored in the patch tables const unsigned int *polygonConnects_orig = farPatchTables->GetFaceVertices(); // use the highest level stored in the patch tables const OpenSubdiv::FarSubdivisionTables<OpenSubdiv::OsdVertex> *farSubdivTables = farMesh->GetSubdivisionTables(); unsigned int numVertices = farSubdivTables->GetNumVertices(subdivisionLevel); unsigned int vertexOffset = farSubdivTables->GetFirstVertexOffset(subdivisionLevel); // Init Maya Data MFloatPointArray points(numVertices); MIntArray faceCounts(numPolygons); // number of edges for each polygon. Assume quads (4-edges per face) MIntArray faceConnects(numPolygons*4); // array of vertex ids for all edges. assuming quads // -- Face Counts for (int i=0; i < numPolygons; ++i) { faceCounts[i] = 4; } // -- Face Connects for (unsigned int i=0; i < faceConnects.length(); i++) { faceConnects[i] = polygonConnects_orig[i] - vertexOffset; // adjust vertex indices so that v0 is at index 0 } // -- Points // Number of floats in each vertex. (positions, normals, etc) int numFloatsPerVertex = vertexBuffer->GetNumElements(); assert(numFloatsPerVertex == 3); // assuming only xyz stored for each vertex const float *vertexData = vertexBuffer->BindCpuBuffer(); float *ptrVertexData; for (unsigned int i=0; i < numVertices; i++) { // make sure to offset to the first osd vertex for that subd level unsigned int osdRawVertexIndex = i + vertexOffset; // Lookup the data in the vertexData ptrVertexData = (float *) vertexData + ((osdRawVertexIndex) * numFloatsPerVertex); points.set(i, ptrVertexData[0], ptrVertexData[1], ptrVertexData[2]); } // Create New Mesh from MFnMesh MFnMesh newMeshFn; MObject newMeshObj = newMeshFn.create(points.length(), faceCounts.length(), points, faceCounts, faceConnects, newMeshDataObj, &returnStatus); MCHECKERR(returnStatus, "Cannot create new mesh"); // Attach UVs (if present) // ASSUMPTION: Only tracking UVs as FVar data. Will need to change this // ASSUMPTION: OSD has a unique UV for each face-vertex int fvarTotalWidth = farMesh->GetTotalFVarWidth(); if (fvarTotalWidth > 0) { // Get face-varying set names and other info from the inMesh MStringArray uvSetNames; MStringArray colorSetNames; std::vector<int> colorSetChannels; std::vector<MFnMesh::MColorRepresentation> colorSetReps; int totalColorSetChannels = 0; returnStatus = getMayaFvarFieldParams(inMeshFn, uvSetNames, colorSetNames, colorSetChannels, colorSetReps, totalColorSetChannels); int numUVSets = uvSetNames.length(); int expectedFvarTotalWidth = numUVSets*2 + totalColorSetChannels; assert(fvarTotalWidth == expectedFvarTotalWidth); const OpenSubdiv::FarPatchTables::FVarDataTable &fvarDataTable = farPatchTables->GetFVarDataTable(); assert(fvarDataTable.size() == expectedFvarTotalWidth*faceConnects.length()); // Create an array of indices to map each face-vert to the UV and ColorSet Data MIntArray fvarConnects(faceConnects.length()); for (unsigned int i=0; i < faceConnects.length(); i++) { fvarConnects[i] = i; } MFloatArray uCoord(faceConnects.length()); MFloatArray vCoord(faceConnects.length()); for (int uvSetIndex=0; uvSetIndex < numUVSets; uvSetIndex++) { for(unsigned int vertid=0; vertid < faceConnects.length(); vertid++) { int fvarItem = vertid*fvarTotalWidth + uvSetIndex*2; // stride per vertex is the fvarTotalWidth uCoord[vertid] = fvarDataTable[fvarItem]; vCoord[vertid] = fvarDataTable[fvarItem+1]; } // Assign UV buffer and map the uvids for each face-vertex if (uvSetIndex != 0) { // assume uvset index 0 is the default UVset, so do not create returnStatus = newMeshFn.createUVSetDataMesh( uvSetNames[uvSetIndex] ); } MCHECKERR(returnStatus, "Cannot create UVSet"); newMeshFn.setUVs(uCoord,vCoord, &uvSetNames[uvSetIndex]); newMeshFn.assignUVs(faceCounts, fvarConnects, &uvSetNames[uvSetIndex]); } MColorArray colorArray(faceConnects.length()); int colorSetRelativeStartIndex = numUVSets*2; for (unsigned int colorSetIndex=0; colorSetIndex < colorSetNames.length(); colorSetIndex++) { for(unsigned int vertid=0; vertid < faceConnects.length(); vertid++) { int fvarItem = vertid*fvarTotalWidth + colorSetRelativeStartIndex; if (colorSetChannels[colorSetIndex] == 1) { colorArray[vertid].r = fvarDataTable[fvarItem]; colorArray[vertid].g = fvarDataTable[fvarItem]; colorArray[vertid].b = fvarDataTable[fvarItem]; colorArray[vertid].a = 1.0f; } else if (colorSetChannels[colorSetIndex] == 3) { colorArray[vertid].r = fvarDataTable[fvarItem]; colorArray[vertid].g = fvarDataTable[fvarItem+1]; colorArray[vertid].b = fvarDataTable[fvarItem+2]; colorArray[vertid].a = 1.0f; } else { colorArray[vertid].r = fvarDataTable[fvarItem]; colorArray[vertid].g = fvarDataTable[fvarItem+1]; colorArray[vertid].b = fvarDataTable[fvarItem+2]; colorArray[vertid].a = fvarDataTable[fvarItem+3]; } } // Assign UV buffer and map the uvids for each face-vertex // API Limitation: Cannot set MColorRepresentation here returnStatus = newMeshFn.createColorSetDataMesh(colorSetNames[colorSetIndex]); MCHECKERR(returnStatus, "Cannot create ColorSet"); bool isColorClamped = inMeshFn.isColorClamped(colorSetNames[colorSetIndex], &returnStatus); newMeshFn.setIsColorClamped(colorSetNames[colorSetIndex], isColorClamped); newMeshFn.setColors(colorArray, &colorSetNames[colorSetIndex], colorSetReps[colorSetIndex]); newMeshFn.assignColors(fvarConnects, &colorSetNames[colorSetIndex]); // Increment colorSet start location in fvar buffer colorSetRelativeStartIndex += colorSetChannels[colorSetIndex]; } } return MS::kSuccess; }