Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

maintain UV connectivity across export and re-import round trips #1175

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 36 additions & 51 deletions lib/mayaUsd/fileio/utils/meshReadUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ bool assignUVSetPrimvarToMesh(const UsdGeomPrimvar& primvar, MFnMesh& meshFn, bo
{
const TfToken& primvarName = primvar.GetPrimvarName();

// Get the raw data before applying any indexing.
VtVec2fArray uvValues;
if (!primvar.Get(&uvValues) || uvValues.empty()) {
TF_WARN(
Expand All @@ -200,53 +199,7 @@ bool assignUVSetPrimvarToMesh(const UsdGeomPrimvar& primvar, MFnMesh& meshFn, bo
return false;
}

// This is the number of UV values assuming the primvar is NOT indexed.
VtIntArray assignmentIndices;
if (primvar.GetIndices(&assignmentIndices)) {
// The primvar IS indexed, so the indices array is what determines the
// number of UV values.
int unauthoredValuesIndex = primvar.GetUnauthoredValuesIndex();

// Replace any index equal to unauthoredValuesIndex with -1.
if (unauthoredValuesIndex != -1) {
for (int& index : assignmentIndices) {
if (index == unauthoredValuesIndex) {
index = -1;
}
}
}

// Furthermore, if unauthoredValuesIndex is valid for uvValues, then
// remove it from uvValues and shift the indices (we don't want to
// import the unauthored value into Maya, where it has no meaning).
if (unauthoredValuesIndex >= 0
&& static_cast<size_t>(unauthoredValuesIndex) < uvValues.size()) {
// This moves [unauthoredValuesIndex + 1, end) to
// [unauthoredValuesIndex, end - 1), erasing the
// unauthoredValuesIndex.
std::move(
uvValues.begin() + unauthoredValuesIndex + 1,
uvValues.end(),
uvValues.begin() + unauthoredValuesIndex);
uvValues.pop_back();

for (int& index : assignmentIndices) {
if (index > unauthoredValuesIndex) {
index = index - 1;
}
}
}
}

// Go through the UV data and add the U and V values to separate
// MFloatArrays.
MFloatArray uCoords;
MFloatArray vCoords;
for (const GfVec2f& v : uvValues) {
uCoords.append(v[0]);
vCoords.append(v[1]);
}

// Determine the name to use for the Maya UV set.
MStatus status { MS::kSuccess };
MString uvSetName(primvarName.GetText());
bool createUVSet = true;
Expand All @@ -260,7 +213,7 @@ bool assignUVSetPrimvarToMesh(const UsdGeomPrimvar& primvar, MFnMesh& meshFn, bo
// If map1 still exists, we rename and re-use it:
MStringArray uvSetNames;
meshFn.getUVSetNames(uvSetNames);
if (uvSetNames[0] == UsdMayaMeshPrimvarTokens->DefaultMayaTexcoordName.GetText()) {
if (uvSetNames[0u] == UsdMayaMeshPrimvarTokens->DefaultMayaTexcoordName.GetText()) {
meshFn.renameUVSet(
UsdMayaMeshPrimvarTokens->DefaultMayaTexcoordName.GetText(), uvSetName);
createUVSet = false;
Expand All @@ -287,8 +240,23 @@ bool assignUVSetPrimvarToMesh(const UsdGeomPrimvar& primvar, MFnMesh& meshFn, bo
MString currentSet = meshFn.currentUVSetName();
meshFn.setCurrentUVSetName(currentSet);

// Create UVs on the mesh from the values we collected out of the primvar.
// We'll assign mesh components to these values below.
// Set the UVs on the mesh from the values we collected out of the primvar.
// We'll check whether there is an unauthored value in the primvar and skip
// it if so to ensure that we don't import it into Maya where it has no
// meaning.
const int unauthoredValuesIndex = primvar.GetUnauthoredValuesIndex();

MFloatArray uCoords;
MFloatArray vCoords;

for (size_t uvId = 0u; uvId < uvValues.size(); ++uvId) {
if (unauthoredValuesIndex < 0 || uvId != static_cast<size_t>(unauthoredValuesIndex)) {
const GfVec2f& v = uvValues[uvId];
uCoords.append(v[0u]);
vCoords.append(v[1u]);
}
}

status = meshFn.setUVs(uCoords, vCoords, &uvSetName);
if (status != MS::kSuccess) {
TF_WARN(
Expand All @@ -298,6 +266,23 @@ bool assignUVSetPrimvarToMesh(const UsdGeomPrimvar& primvar, MFnMesh& meshFn, bo
return false;
}

VtIntArray assignmentIndices;
if (primvar.GetIndices(&assignmentIndices)) {
if (unauthoredValuesIndex >= 0) {
// Since the unauthored value was removed above, we need to fix up
// the assignment indices to replace any index equal to the
// unauthored value index with -1, and decrement any index that was
// after the unauthored value index by 1.
for (int& index : assignmentIndices) {
if (index == unauthoredValuesIndex) {
index = -1;
} else if (index > unauthoredValuesIndex) {
index -= 1;
}
}
}
}

const TfToken& interpolation = primvar.GetInterpolation();

// Build an array of value assignments for each face vertex in the mesh.
Expand Down
62 changes: 37 additions & 25 deletions lib/mayaUsd/fileio/utils/meshWriteUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <pxr/base/tf/staticTokens.h>
#include <pxr/base/tf/token.h>
#include <pxr/base/vt/array.h>
#include <pxr/pxr.h>
#include <pxr/usd/usdGeom/mesh.h>
#include <pxr/usd/usdGeom/pointBased.h>
#include <pxr/usd/usdGeom/primvar.h>
Expand Down Expand Up @@ -846,39 +847,51 @@ bool UsdMayaMeshWriteUtils::getMeshUVSetData(
TfToken* interpolation,
VtIntArray* assignmentIndices)
{
MStatus status { MS::kSuccess };

// Sanity check first to make sure this UV set even has assigned values
// before we attempt to do anything with the data.
// Check first to make sure this UV set even has assigned values before we
// attempt to do anything with the data. We cannot directly use this data
// otherwise though since we need a uvId for every face vertex, and the
// returned uvIds MIntArray may be shorter than that if there are unmapped
// faces.
MIntArray uvCounts, uvIds;
status = mesh.getAssignedUVs(uvCounts, uvIds, &uvSetName);
if (status != MS::kSuccess) {
return false;
}
if (uvCounts.length() == 0 || uvIds.length() == 0) {
MStatus status = mesh.getAssignedUVs(uvCounts, uvIds, &uvSetName);
CHECK_MSTATUS_AND_RETURN(status, false);

if (uvCounts.length() == 0u || uvIds.length() == 0u) {
return false;
}

// using itFV.getUV() does not always give us the right answer, so
// instead, we have to use itFV.getUVIndex() and use that to index into the
// UV set.
// Transfer the UV values directly to USD, in the same order as they are in
// the Maya mesh.
MFloatArray uArray;
MFloatArray vArray;
mesh.getUVs(uArray, vArray, &uvSetName);
status = mesh.getUVs(uArray, vArray, &uvSetName);
CHECK_MSTATUS_AND_RETURN(status, false);

if (uArray.length() != vArray.length()) {
return false;
}

// 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.
const unsigned int numFaceVertices = mesh.numFaceVertices(&status);
uvArray->clear();
assignmentIndices->assign((size_t)numFaceVertices, -1);
uvArray->reserve(static_cast<size_t>(uArray.length()));
for (unsigned int uvId = 0u; uvId < uArray.length(); ++uvId) {
#if PXR_VERSION >= 2011
uvArray->emplace_back(uArray[uvId], vArray[uvId]);
#else
GfVec2f value(uArray[uvId], vArray[uvId]);
uvArray->push_back(value);
#endif
}

// Now iterate through all the face vertices and fill in the faceVarying
// assignmentIndices array, again in the same order as in the Maya mesh.
const unsigned int numFaceVertices = mesh.numFaceVertices(&status);
CHECK_MSTATUS_AND_RETURN(status, false);

assignmentIndices->assign(static_cast<size_t>(numFaceVertices), -1);
*interpolation = UsdGeomTokens->faceVarying;

MItMeshFaceVertex itFV(mesh.object());
unsigned int fvi = 0;
unsigned int fvi = 0u;
for (itFV.reset(); !itFV.isDone(); itFV.next(), ++fvi) {
if (!itFV.hasUVs(uvSetName)) {
// No UVs for this faceVertex, so leave it unassigned.
Expand All @@ -887,17 +900,16 @@ bool UsdMayaMeshWriteUtils::getMeshUVSetData(

int uvIndex;
itFV.getUVIndex(uvIndex, &uvSetName);
if (uvIndex < 0 || static_cast<size_t>(uvIndex) >= uArray.length()) {
if (uvIndex < 0 || static_cast<unsigned int>(uvIndex) >= uArray.length()) {
return false;
}

GfVec2f value(uArray[uvIndex], vArray[uvIndex]);
uvArray->push_back(value);
(*assignmentIndices)[fvi] = uvArray->size() - 1;
(*assignmentIndices)[fvi] = uvIndex;
}

UsdMayaUtil::MergeEquivalentIndexedValues(uvArray, assignmentIndices);
UsdMayaUtil::CompressFaceVaryingPrimvarIndices(mesh, interpolation, assignmentIndices);
// We do not merge indexed values or compress indices here in an effort to
// maintain the same UV shells and connectivity across export/import
// round-trips.

return true;
}
Expand Down
Loading