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

EMSUSD-1712 Reimport blendshape issues #3954

Merged
merged 4 commits into from
Oct 10, 2024
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
4 changes: 4 additions & 0 deletions lib/mayaUsd/fileio/translators/translatorBlendShape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ bool UsdMayaTranslatorBlendShape::Read(const UsdPrim& meshPrim, UsdMayaPrimReade
const auto blendShapeObj
= blendFn.create(objToBlendShape, MFnBlendShapeDeformer::kLocalOrigin, &status);
CHECK_MSTATUS_AND_RETURN(status, false)
MFnDependencyNode blendShapeDepNodeFn;
blendShapeDepNodeFn.setObject(blendShapeObj);
blendShapeDepNodeFn.setName(MString(
TfStringPrintf("%s_Deformer", meshPrim.GetPath().GetElementString().c_str()).c_str()));

MObject deformedMeshObject;
VtVec3fArray deltaPoints, deltaNormals;
Expand Down
27 changes: 22 additions & 5 deletions lib/mayaUsd/fileio/translators/translatorSkel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <mayaUsd/fileio/translators/translatorUtil.h>
#include <mayaUsd/fileio/translators/translatorXformable.h>
#include <mayaUsd/fileio/utils/meshWriteUtils.h>
#include <mayaUsd/undo/OpUndoItems.h>
#include <mayaUsd/utils/util.h>

Expand All @@ -28,21 +29,18 @@
#include <pxr/usd/usdSkel/topology.h>

#include <maya/MDGModifier.h>
#include <maya/MDagModifier.h>
#include <maya/MDoubleArray.h>
#include <maya/MEulerRotation.h>
#include <maya/MFnAnimCurve.h>
#include <maya/MFnBlendShapeDeformer.h>
#include <maya/MFnComponentListData.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnDoubleArrayData.h>
#include <maya/MFnMatrixData.h>
#include <maya/MFnMesh.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnSingleIndexedComponent.h>
#include <maya/MFnSkinCluster.h>
#include <maya/MFnTransform.h>
#include <maya/MMatrix.h>
#include <maya/MObjectHandle.h>
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>

Expand Down Expand Up @@ -1317,8 +1315,27 @@ bool UsdMayaTranslatorSkel::CreateSkinCluster(

const auto skinSrc = shapeToSkinInMesh.source();
if (!skinSrc.isNull()) {
MObjectArray blendShapeDeformers;
UsdMayaMeshWriteUtils::getBlendShapesOfMesh(
shapeToSkin, blendShapeDeformers, &status);

dgMod.disconnect(skinSrc, shapeToSkinInMesh);
status = dgMod.connect(skinSrc, groupPartsInputGeometry);

// If the mesh has blendShapes, it has already been created prior to this step.
// When that's the case, we need to disconnect the blendShape outputGeometry
// and connect it to the groupParts.inputGeometry which drives the skel/skinCluster.
// Then, the output of the groupParts will be connected to the end mesh.
if (blendShapeDeformers.length() > 0) {
MFnBlendShapeDeformer blendShapeFn(blendShapeDeformers[0], &status);
MPlug blenshapeOutputPlug
= blendShapeFn.findPlug(_MayaTokens->outputGeometry, &status);
MPlug blenshapeOutputPlug0
= blenshapeOutputPlug.elementByLogicalIndex(0, &status);

status = dgMod.connect(blenshapeOutputPlug0, groupPartsInputGeometry);
} else {
status = dgMod.connect(skinSrc, groupPartsInputGeometry);
}
CHECK_MSTATUS_AND_RETURN(status, false);
} else {
// For the case where there were no blendShapes attached to the mesh, create a rest
Expand Down
30 changes: 30 additions & 0 deletions lib/mayaUsd/fileio/utils/meshWriteUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,36 @@ MStatus UsdMayaMeshWriteUtils::getSkinClustersUpstreamOfMesh(
return stat;
}

void UsdMayaMeshWriteUtils::getBlendShapesOfMesh(
const MObject& mesh,
MObjectArray& blendShapes,
MStatus* stat)
{
if (mesh.isNull() || !mesh.hasFn(MFn::kMesh)) {
if (stat) {
*stat = MStatus::kInvalidParameter;
}
return;
}

blendShapes.clear();
MObject searchObj = MObject(mesh);
MItDependencyGraph itDg(
searchObj,
MFn::kInvalid,
MItDependencyGraph::kUpstream,
MItDependencyGraph::kDepthFirst,
MItDependencyGraph::kNodeLevel,
stat);
while (!itDg.isDone()) {
MObject curNode = itDg.currentItem();
if (curNode.hasFn(MFn::kBlendShape)) {
blendShapes.append(curNode);
}
itDg.next();
}
}

MBoundingBox UsdMayaMeshWriteUtils::calcBBoxOfMeshes(const MObjectArray& meshes)
{
unsigned int numMeshes = meshes.length();
Expand Down
6 changes: 4 additions & 2 deletions lib/mayaUsd/fileio/utils/meshWriteUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,17 @@ MStatus getSkinClusterConnectedToMesh(const MObject& mesh, MObject& skinCluster)
MAYAUSD_CORE_PUBLIC
MStatus getSkinClustersUpstreamOfMesh(const MObject& mesh, MObjectArray& skinClusters);

MAYAUSD_CORE_PUBLIC
void getBlendShapesOfMesh(const MObject& mesh, MObjectArray& blendShapes, MStatus* stat);

/**
* Calculates the union bounding box of a given array of meshes.
*
* @param meshes The meshes to calculate the union bounding box of.
*
* @return The union bounding box.
*/
MAYAUSD_CORE_PUBLIC
MBoundingBox calcBBoxOfMeshes(const MObjectArray& meshes);
MAYAUSD_CORE_PUBLIC MBoundingBox calcBBoxOfMeshes(const MObjectArray& meshes);

/// Helper method for getting Maya mesh normals as a VtVec3fArray.
MAYAUSD_CORE_PUBLIC
Expand Down
13 changes: 3 additions & 10 deletions lib/usd/translators/meshWriterBlendShapes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,16 +666,9 @@ MObject PxrUsdTranslators_MeshWriter::writeBlendShapeData(UsdGeomMesh& primSchem
MString curTargetLongNameMStr;
if (!targetMesh.isNull()) {
MFnDagNode dagNode(targetMesh);
MString nodeName;
if (dagNode.parentCount() > 0) {
MFnDagNode parentDagNode(dagNode.parent(0));
curTargetNameMStr
= UsdMayaUtil::GetUniqueNameOfDagNode(parentDagNode.object());
curTargetLongNameMStr = curTargetNameMStr;
} else {
curTargetNameMStr = UsdMayaUtil::GetUniqueNameOfDagNode(targetMesh);
curTargetLongNameMStr = curTargetNameMStr;
}
curTargetNameMStr = UsdMayaUtil::GetUniqueNameOfDagNode(targetMesh);
curTargetLongNameMStr = curTargetNameMStr;

// NOTE: (yliangsiew) Because UsdSkelBlendShape does not
// support animated targets (the `normalOffsets` and
// `offsets` attributes are defined as uniforms), we cannot
Expand Down
4 changes: 2 additions & 2 deletions test/lib/usd/translators/testUsdExportBlendshapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def testBlendShapesExport(self):
cmds.mayaUSDExport(f=temp_file, v=True, sl=True, ebs=True, skl="auto")

stage = Usd.Stage.Open(temp_file)
prim = stage.GetPrimAtPath("/root/base/blend")
prim = stage.GetPrimAtPath("/root/base/blendShape")
offsets = prim.GetAttribute("offsets").Get()

for i, coords in enumerate(offsets):
Expand All @@ -68,7 +68,7 @@ def testBlendShapesExport(self):
"""
Sample BlendShape prim:
def BlendShape "blend"
def BlendShape "blendShape"
{
uniform vector3f[] normalOffsets = [(0, 0, 0), (0, 0, 0), (0, 0, 0)]
uniform vector3f[] offsets = [(0, -0.25, 0), (0, -0.25, 0), (0, 0.25, 0)]
Expand Down
14 changes: 7 additions & 7 deletions test/lib/usd/translators/testUsdImportBlendShapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,28 +66,28 @@ def test_BlendShapesImport(self):
skinningQuery = skelCache.GetSkinningQuery(meshPrim)
self.assertTrue(skinningQuery)

self.assertEqual(cmds.nodeType("blendShape1"), "blendShape")
self.assertEqual(cmds.nodeType("b1_Deformer"), "blendShape")

regularShape = sorted(
cmds.listConnections(
"blendShape1.inputTarget[0].inputTargetGroup[0].inputTargetItem[6000].inputGeomTarget",
"b1_Deformer.inputTarget[0].inputTargetGroup[0].inputTargetItem[6000].inputGeomTarget",
destination=False, source=True, plugs=True))
self.assertEqual(regularShape, ['Box0002.worldMesh'])

inBetween = sorted(
cmds.listConnections(
"blendShape1.inputTarget[0].inputTargetGroup[0].inputTargetItem[4000].inputGeomTarget",
"b1_Deformer.inputTarget[0].inputTargetGroup[0].inputTargetItem[4000].inputGeomTarget",
destination=False, source=True, plugs=True))
self.assertEqual(inBetween, ['IBT_1.worldMesh'])

cmds.currentTime(0)
self.assertEqual(sorted(cmds.getAttr("blendShape1.weight")[0]), [-1.0])
self.assertEqual(sorted(cmds.getAttr("b1_Deformer.weight")[0]), [-1.0])
cmds.currentTime(3)
self.assertEqual(sorted(cmds.getAttr("blendShape1.weight")[0]), [-0.14404296875])
self.assertEqual(sorted(cmds.getAttr("b1_Deformer.weight")[0]), [-0.14404296875])
cmds.currentTime(5)
self.assertEqual(sorted(cmds.getAttr("blendShape1.weight")[0]), [0.13427734375])
self.assertEqual(sorted(cmds.getAttr("b1_Deformer.weight")[0]), [0.13427734375])
cmds.currentTime(8)
self.assertEqual(sorted(cmds.getAttr("blendShape1.weight")[0]), [1.0])
self.assertEqual(sorted(cmds.getAttr("b1_Deformer.weight")[0]), [1.0])


if __name__ == '__main__':
Expand Down