1616//
1717#include " translatorBlendShape.h"
1818
19+ #include < mayaUsd/utils/util.h>
20+
1921#include < pxr/base/tf/diagnostic.h>
22+ #include < pxr/base/tf/stringUtils.h>
2023#include < pxr/base/vt/types.h>
2124#include < pxr/usd/usd/common.h>
25+ #include < pxr/usd/usdGeom/metrics.h>
2226#include < pxr/usd/usdSkel/animQuery.h>
2327#include < pxr/usd/usdSkel/animation.h>
2428#include < pxr/usd/usdSkel/binding.h>
2529#include < pxr/usd/usdSkel/bindingAPI.h>
2630#include < pxr/usd/usdSkel/blendShape.h>
2731#include < pxr/usd/usdSkel/root.h>
2832
33+ #include < maya/MDistance.h>
2934#include < maya/MFloatVectorArray.h>
3035#include < maya/MFnAnimCurve.h>
3136#include < maya/MFnBlendShapeDeformer.h>
37+ #include < maya/MFnComponentListData.h>
3238#include < maya/MFnGeometryFilter.h>
39+ #include < maya/MFnPointArrayData.h>
40+ #include < maya/MFnSingleIndexedComponent.h>
41+ #include < maya/MGlobal.h>
3342#include < maya/MItDependencyGraph.h>
3443#include < maya/MPointArray.h>
3544#include < maya/MStatus.h>
3948PXR_NAMESPACE_USING_DIRECTIVE
4049
4150namespace MAYAUSD_NS_DEF {
51+
52+ struct _BlendShapeAttributesNames
53+ {
54+ const char * it { " inputTarget" };
55+ const char * itg { " inputTargetGroup" };
56+ const char * iti { " inputTargetItem" };
57+ const char * ipt { " inputPointsTarget" };
58+ const char * ict { " inputComponentsTarget" };
59+ const char * sn { " supportNegativeWeights" };
60+ const char * w { " weight" };
61+ const char * ibig { " inbetweenInfoGroup" };
62+ const char * ibi { " inbetweenInfo" };
63+ const char * ibtn { " inbetweenTargetName" };
64+ };
65+
66+ TfStaticData<_BlendShapeAttributesNames> _BlendShapeAttributes;
67+
4268void _AddBlendShape (
43- MFnBlendShapeDeformer& blendShapeFn,
44- const MObject& originalShape,
45- const MObject& parentTransform,
46- const MPointArray& originalPoints,
47- const GfVec3f* originalNormals,
48- const VtIntArray& pointIndices,
49- const std::string name,
50- const VtVec3fArray offsetArray,
51- const VtVec3fArray& normalsOffsetArray,
52- int blendShapeTargetIndex,
53- float weight)
69+ const MFnBlendShapeDeformer& fnBlendShape,
70+ const std::string& blendShapeName,
71+ unsigned int targetIdx,
72+ float weight,
73+ const MPointArray& deltas,
74+ MIntArray& indices,
75+ bool isInBetween = false )
5476{
55- if (offsetArray.size () != pointIndices.size ()) {
56- TF_RUNTIME_ERROR (
57- " BlendShape <%s> doesn't match the number of offset points." , name.c_str ());
58- return ;
59- }
77+ const auto blendShapeObj = fnBlendShape.object ();
6078
61- MFnMesh blendShapeMeshFn;
62- MStatus status;
63- auto newShape = blendShapeMeshFn.copy (originalShape, parentTransform, &status);
64-
65- MPointArray deltaPoints (originalPoints);
66- for (size_t pidx = 0 ; pidx < pointIndices.size (); ++pidx) {
67- // USD BlendShapes doesn't require to all points to have a delta.
68- // Thus, given the indicesArray, find the actual index (index) in the original shape that
69- // will be affected that the offset on that particular position.
70- int index = pointIndices[pidx];
71- MPoint original = originalPoints[index];
72- GfVec3f offset = offsetArray[pidx];
73-
74- deltaPoints.set (
75- MPoint (original.x + offset[0 ], original.y + offset[1 ], original.z + offset[2 ]), index);
79+ auto inputTargetAttr = fnBlendShape.attribute (_BlendShapeAttributes->it );
80+ auto inputTargetGroupAttr = fnBlendShape.attribute (_BlendShapeAttributes->itg );
81+ auto inputTargetItemAttr = fnBlendShape.attribute (_BlendShapeAttributes->iti );
82+ auto inputPointsTargetAttr = fnBlendShape.attribute (_BlendShapeAttributes->ipt );
83+ auto inputComponentTargetAttr = fnBlendShape.attribute (_BlendShapeAttributes->ict );
84+ auto inputWeightAttr = fnBlendShape.attribute (_BlendShapeAttributes->w );
85+
86+ if (weight < 0 .f ) {
87+ auto supportNegativeWeightAttr = fnBlendShape.attribute (_BlendShapeAttributes->sn );
88+
89+ MPlug plgSupportNegativeWeight (blendShapeObj, supportNegativeWeightAttr);
90+ plgSupportNegativeWeight.setBool (true );
7691 }
77- blendShapeMeshFn.setPoints (deltaPoints);
78-
79- // Applying blendShape normals
80- if (!normalsOffsetArray.empty ()) {
81- MFloatVectorArray deltaNormals (normalsOffsetArray.size ());
82- for (size_t i = 0 ; i < normalsOffsetArray.size (); ++i) {
83- MFloatVector deltaNormal (
84- originalNormals[i][0 ], originalNormals[i][1 ], originalNormals[i][2 ]);
85-
86- deltaNormal.x += normalsOffsetArray[i][0 ];
87- deltaNormal.y += normalsOffsetArray[i][1 ];
88- deltaNormal.z += normalsOffsetArray[i][2 ];
89- deltaNormals.set (deltaNormal, i);
90- }
91- blendShapeMeshFn.setNormals (deltaNormals);
92+
93+ // The weight index is an integer that maps weight values to Maya's internal representation
94+ // Maya supports negative weights, so we need to handle the full range
95+ // Positive weights: 5000-6000 (weight 0.001 to 1.0)
96+ // Negative weights: 0-4999 (weight -5.0 to -0.001)
97+ // Zero weight: 5000
98+ static const auto convertWeightToIndex = [](float weight) -> int {
99+ int result = 5000 + static_cast <int >(weight * 1000 .f );
100+ return result >= 0 ? result : 0 ;
101+ };
102+
103+ // Maya's blendShape api requires a copy of the original shape or a copy of the target mesh
104+ // for it to work.
105+ // To avoid that, use plugs to manually create the blendShape targets using only the deltas
106+ // from USD.
107+
108+ // Create the plug for the desired attribute: .it[-1].itg[-1].iti[-1].ipt
109+ MPlug plgInPoints (blendShapeObj, inputPointsTargetAttr);
110+ // Set the first element position in the plug: .it[0].itg[-1].iti[-1].ipt
111+ plgInPoints.selectAncestorLogicalIndex (0 , inputTargetAttr);
112+ // Set the second plug element: .it[0].itg[targetIdx].iti[-1].ipt
113+ plgInPoints.selectAncestorLogicalIndex (targetIdx, inputTargetGroupAttr);
114+ // Third element: .it[0].itg[targetIdx].iti[convertWeightToIndex(weight)].ipt
115+ plgInPoints.selectAncestorLogicalIndex (convertWeightToIndex (weight), inputTargetItemAttr);
116+
117+ {
118+ MFnPointArrayData data;
119+ MObject dataObj = data.create ();
120+ data.set (deltas);
121+ plgInPoints.setValue (dataObj);
92122 }
93123
94- MFnDependencyNode ibDepNodeFn;
95- ibDepNodeFn.setObject (newShape);
96- ibDepNodeFn.setName (MString (name.c_str ()));
124+ // Now, similarly, set the inputComponentsTarget attribute (which are the indices being changed)
125+ MPlug plgInCompTarget (blendShapeObj, inputComponentTargetAttr);
126+ plgInCompTarget.selectAncestorLogicalIndex (0 , inputTargetAttr);
127+ plgInCompTarget.selectAncestorLogicalIndex (targetIdx, inputTargetGroupAttr);
128+ plgInCompTarget.selectAncestorLogicalIndex (convertWeightToIndex (weight), inputTargetItemAttr);
129+ {
130+ MFnSingleIndexedComponent indexList (blendShapeObj);
131+ auto indexListObj = indexList.create (MFn::kMeshVertComponent );
132+ indexList.addElements (indices);
133+ MFnComponentListData fnComp;
134+ MObject compObj = fnComp.create ();
135+ fnComp.add (indexListObj);
136+ plgInCompTarget.setValue (compObj);
137+ }
97138
98- blendShapeFn.addTarget (originalShape, blendShapeTargetIndex, newShape, weight);
99- blendShapeMeshFn.setIntermediateObject (true );
139+ // If weight is not 1.0, that means we are creating an inbetween shape
140+ if (isInBetween) {
141+ auto inBetweenTargetNameAttr = fnBlendShape.attribute (_BlendShapeAttributes->ibtn );
142+ auto inBetweenInfoGroupAttr = fnBlendShape.attribute (_BlendShapeAttributes->ibig );
143+ auto inBetweenInfoAttr = fnBlendShape.attribute (_BlendShapeAttributes->ibi );
144+ MPlug plgInBetweenTargetName (blendShapeObj, inBetweenTargetNameAttr);
145+ plgInBetweenTargetName.selectAncestorLogicalIndex (targetIdx, inBetweenInfoGroupAttr);
146+ plgInBetweenTargetName.selectAncestorLogicalIndex (
147+ convertWeightToIndex (weight), inBetweenInfoAttr);
148+
149+ // USD inbetweens are named: "inbetween:<name>"
150+ // Remove the inbetween prefix
151+ auto inBetweenName = blendShapeName.substr (blendShapeName.find_first_of (" :" ) + 1 );
152+
153+ plgInBetweenTargetName.setString (MString (inBetweenName.c_str ()));
154+ } else {
155+ MPlug plgWeight (blendShapeObj, inputWeightAttr);
156+ plgWeight.selectAncestorLogicalIndex (targetIdx, inputWeightAttr);
157+
158+ MString cmd;
159+ cmd.format (
160+ " import maya.cmds as cmds; cmds.aliasAttr(\" ^1s\" ,\" ^2s\" );" ,
161+ blendShapeName.c_str (),
162+ plgWeight.name ().asChar ());
163+
164+ MGlobal::executePythonCommand (cmd);
165+ }
100166}
101167
102168bool UsdMayaTranslatorBlendShape::Read (const UsdPrim& meshPrim, UsdMayaPrimReaderContext* context)
@@ -137,13 +203,6 @@ bool UsdMayaTranslatorBlendShape::Read(const UsdPrim& meshPrim, UsdMayaPrimReade
137203 MFnMesh originalMeshFn (originalShape);
138204 MPointArray originalPoints;
139205 originalMeshFn.getPoints (originalPoints);
140- const size_t originalNumVertices = originalPoints.length ();
141-
142- // There's no easy way to access the mesh's original normals.
143- // This is how it's done in other places when those are needed.
144- const float * normals = originalMeshFn.getRawNormals (&status);
145- CHECK_MSTATUS_AND_RETURN (status, false )
146- const GfVec3f* originalNormals = reinterpret_cast <const GfVec3f*>(normals);
147206
148207 MFnBlendShapeDeformer blendFn;
149208 const auto blendShapeObj
@@ -154,14 +213,12 @@ bool UsdMayaTranslatorBlendShape::Read(const UsdPrim& meshPrim, UsdMayaPrimReade
154213 blendShapeDepNodeFn.setName (MString (
155214 TfStringPrintf (" %s_Deformer" , meshPrim.GetPath ().GetElementString ().c_str ()).c_str ()));
156215
157- MObject deformedMeshObject;
158- VtVec3fArray deltaPoints, deltaNormals;
159- VtIntArray pointIndices;
160216 for (size_t targetIdx = 0 ; targetIdx < blendShapeTargets.size (); ++targetIdx) {
161- const SdfPath blendShapePath = blendShapeTargets[targetIdx];
162- UsdSkelBlendShape blendShape (stage->GetPrimAtPath (blendShapePath));
163- std::string blendShapeName = blendShape.GetPath ().GetElementString ();
217+ const SdfPath& blendShapePath = blendShapeTargets[targetIdx];
218+ const UsdSkelBlendShape blendShape (stage->GetPrimAtPath (blendShapePath));
219+ const std::string blendShapeName = blendShape.GetPath ().GetElementString ();
164220
221+ VtVec3fArray deltaPoints;
165222 blendShape.GetOffsetsAttr ().Get (&deltaPoints);
166223 if (deltaPoints.empty ()) {
167224 TF_RUNTIME_ERROR (
@@ -171,46 +228,55 @@ bool UsdMayaTranslatorBlendShape::Read(const UsdPrim& meshPrim, UsdMayaPrimReade
171228 continue ;
172229 }
173230
174- blendShape. GetNormalOffsetsAttr (). Get (&deltaNormals) ;
231+ VtIntArray pointIndices ;
175232 blendShape.GetPointIndicesAttr ().Get (&pointIndices);
176233
177234 // Indices aren't authored. Use mesh default pointIndices
178235 if (pointIndices.empty ()) {
179- pointIndices.resize (originalNumVertices );
180- for (size_t i = 0 ; i < originalNumVertices ; ++i) {
181- pointIndices[i] = i ;
236+ pointIndices.reserve (deltaPoints. size () );
237+ for (size_t i = 0 ; i < deltaPoints. size () ; ++i) {
238+ pointIndices. emplace_back (i) ;
182239 }
183240 }
184241
242+ if (deltaPoints.size () != pointIndices.size ()) {
243+ TF_RUNTIME_ERROR (
244+ " BlendShape <%s> for mesh <%s> has mismatched number of delta points and indices." ,
245+ blendShapePath.GetText (),
246+ meshPrim.GetPath ().GetText ());
247+ continue ;
248+ }
249+
250+ // Convert Usd Arrays to Maya Arrays
251+
252+ MIntArray indicesIntArray (static_cast <unsigned int >(pointIndices.size ()));
253+ for (size_t i = 0 ; i < pointIndices.size (); ++i) {
254+ indicesIntArray.set (pointIndices[i], static_cast <unsigned int >(i));
255+ }
256+
257+ MPointArray deltasPointsArray (deltaPoints.size ());
258+ for (size_t pidx = 0 ; pidx < pointIndices.size (); ++pidx) {
259+ const GfVec3f& offset = deltaPoints[pidx];
260+ deltasPointsArray.set (MPoint (offset[0 ], offset[1 ], offset[2 ]), pidx);
261+ }
262+
185263 _AddBlendShape (
186264 blendFn,
187- originalShape,
188- parentTransform,
189- originalPoints,
190- originalNormals,
191- pointIndices,
192265 blendShapeName,
193- deltaPoints,
194- deltaNormals,
195- targetIdx,
196- 1 .0f ); // The original blendShape always has full weight of 1.0f
197-
198- // After adding the main blendShape - now add all the in-betweens
266+ static_cast <unsigned int >(targetIdx),
267+ 1 .f ,
268+ deltasPointsArray,
269+ indicesIntArray);
199270
271+ MIntArray inBetweenIndicesIntArray (indicesIntArray);
200272 const std::vector<UsdSkelInbetweenShape> inBetweens = blendShape.GetInbetweens ();
201- // no in-betweens, can continue to the next blendShape
202- if (inBetweens.empty ()) {
203- continue ;
204- }
205-
206- for (size_t ib = 0 ; ib < inBetweens.size (); ++ib) {
207- const auto & currentInBetween = inBetweens[ib];
208- const std::string inBetweenName = currentInBetween.GetAttr ().GetName ().GetString ();
273+ for (const auto & inBetween : inBetweens) {
274+ const std::string inBetweenName = inBetween.GetAttr ().GetName ().GetString ();
209275 float ibWeight = 0 .f ;
210- currentInBetween .GetWeight (&ibWeight);
276+ inBetween .GetWeight (&ibWeight);
211277
212- VtVec3fArray inBetweenDeltaPoints, inBetweenNormals ;
213- currentInBetween .GetOffsets (&inBetweenDeltaPoints);
278+ VtVec3fArray inBetweenDeltaPoints;
279+ inBetween .GetOffsets (&inBetweenDeltaPoints);
214280 if (inBetweenDeltaPoints.empty ()) {
215281 TF_RUNTIME_ERROR (
216282 " InBetween BlendShape <%s> for mesh <%s> has no delta points." ,
@@ -219,21 +285,29 @@ bool UsdMayaTranslatorBlendShape::Read(const UsdPrim& meshPrim, UsdMayaPrimReade
219285 continue ;
220286 }
221287
222- currentInBetween.GetNormalOffsets (&inBetweenNormals);
288+ if (inBetweenDeltaPoints.size () != pointIndices.size ()) {
289+ TF_RUNTIME_ERROR (
290+ " InBetween BlendShape <%s> for mesh <%s> has a different number of delta "
291+ " points and indices." ,
292+ inBetweenName.c_str (),
293+ meshPrim.GetPath ().GetText ());
294+ continue ;
295+ }
296+
297+ MPointArray inBetweenDeltasPointsArray (inBetweenDeltaPoints.size ());
298+ for (size_t pidx = 0 ; pidx < pointIndices.size (); ++pidx) {
299+ const GfVec3f& offset = inBetweenDeltaPoints[pidx];
300+ inBetweenDeltasPointsArray.set (MPoint (offset[0 ], offset[1 ], offset[2 ]), pidx);
301+ }
223302
224- // InBetween are new blendShapes added to the same target index
225303 _AddBlendShape (
226304 blendFn,
227- originalShape,
228- parentTransform,
229- originalPoints,
230- originalNormals,
231- pointIndices,
232305 inBetweenName,
233- inBetweenDeltaPoints,
234- inBetweenNormals,
235- targetIdx,
236- ibWeight);
306+ static_cast <unsigned int >(targetIdx),
307+ ibWeight,
308+ inBetweenDeltasPointsArray,
309+ inBetweenIndicesIntArray,
310+ true );
237311 }
238312 }
239313 return true ;
0 commit comments