PXR_NAMESPACE_CLOSE_SCOPE // ===================================================================== // // Feel free to add custom code below this line, it will be preserved by // the code generator. The entry point for your custom code should look // minimally like the following: // // WRAP_CUSTOM { // _class // .def("MyCustomMethod", ...) // ; // } // // Of course any other ancillary or support code may be provided. // // Just remember to wrap code in the appropriate delimiters: // 'PXR_NAMESPACE_OPEN_SCOPE', 'PXR_NAMESPACE_CLOSE_SCOPE'. // // ===================================================================== // // --(BEGIN CUSTOM CODE)-- #include "pxr/base/tf/pyEnum.h" PXR_NAMESPACE_OPEN_SCOPE static GfMatrix4d _GetLocalTransformation1(const UsdGeomXformable &self, const UsdTimeCode time) { GfMatrix4d result(1); bool resetsXformStack; self.GetLocalTransformation(&result, &resetsXformStack, time); return result; }
static std::vector<double> _GetTimeSamples(const UsdGeomXformable &self) { std::vector<double> result; self.GetTimeSamples(&result); return result; }
//! [CreateMatrixWithDefault] bool CreateMatrixWithDefault(UsdGeomXformable const &gprim, GfMatrix4d const &defValue) { if (UsdGeomXformOp transform = gprim.MakeMatrixXform()){ return transform.Set(defValue, UsdTimeCode::Default()); } else { return false; } }
static GfMatrix4d _GetLocalTransformation1(const UsdGeomXformable &self, const UsdTimeCode time) { GfMatrix4d result(1); bool resetsXformStack; self.GetLocalTransformation(&result, &resetsXformStack, time); return result; }
static GfMatrix4d _GetLocalTransformation2(const UsdGeomXformable &self, const std::vector<UsdGeomXformOp> &ops, const UsdTimeCode time) { GfMatrix4d result(1); bool resetsXformStack; self.GetLocalTransformation(&result, &resetsXformStack, ops, time); return result; }
//! [CreateAnimatedTransform] bool CreateAnimatedTransform(UsdGeomXformable const &gprim, GfVec3d const &baseTranslate, GfVec3f const &baseRotateXYZ, GfVec3f const &defPivot) { // Only need to do this if you're overriding an existing scene if (not gprim.ClearXformOpOrder()){ return false; } static const TfToken pivSuffix("pivot"); UsdGeomXformOp trans = gprim.AddTranslateOp(); UsdGeomXformOp pivot = gprim.AddTranslateOp(UsdGeomXformOp::PrecisionFloat, pivSuffix); UsdGeomXformOp rotate = gprim.AddRotateXYZOp(); UsdGeomXformOp pivotInv = gprim.AddTranslateOp(UsdGeomXformOp::PrecisionFloat, pivSuffix, /* isInverseOp = */ true); // Now that we have created all the ops, set default values. // Note that we do not need to (and cannot) set the value // for the pivot's inverse op. // For didactic brevity we are eliding success return value checks, // but would absolutely have them in exporters! trans.Set(baseTranslate, UsdTimeCode::Default()); pivot.Set(defPivot, UsdTimeCode::Default()); rotate.Set(baseRotateXYZ, UsdTimeCode::Default()); // Now animate the translation and rotation over a fixed interval with // cheesy linear animation. GfVec3d position(baseTranslate); GfVec3f rotation(baseRotateXYZ); for (double frame = 0; frame < 100.0; frame += 1.0){ trans.Set(position, frame); rotate.Set(rotation, frame); position[0] += 5.0; rotation[2] += 7.0; } return true; }
UsdGeomXformable::XformQuery::XformQuery(const UsdGeomXformable &xformable): _resetsXformStack(false) { vector<UsdGeomXformOp> orderedXformOps = xformable.GetOrderedXformOps(&_resetsXformStack); if (not orderedXformOps.empty()) { _xformOps = orderedXformOps; // Create attribute queries for all the xform ops. TF_FOR_ALL(it, _xformOps) { it->_CreateAttributeQuery(); } }
static UsdAttribute _CreateXformOpOrderAttr(UsdGeomXformable &self, object defaultVal, bool writeSparsely) { return self.CreateXformOpOrderAttr( UsdPythonToSdfType(defaultVal, SdfValueTypeNames->TokenArray), writeSparsely); }
static std::vector<UsdGeomXformOp> _GetOrderedXformOps(const UsdGeomXformable &self) { bool resetsXformStack=false; return self.GetOrderedXformOps(&resetsXformStack); }
// Populate the AnimationChannel vector with various ops based on the Maya // transformation logic If scale and/or rotate pivot are declared, create // inverse ops in the appropriate order void MayaTransformWriter::pushTransformStack( const MFnTransform& iTrans, const UsdGeomXformable& usdXformable, bool writeAnim) { // NOTE: I think this logic and the logic in MayaTransformReader // should be merged so the concept of "CommonAPI" stays centralized. // // By default we assume that the xform conforms to the common API // (xlate,pivot,rotate,scale,pivotINVERTED) As soon as we encounter any // additional xform (compensation translates for pivots, rotateAxis or // shear) we are not conforming anymore bool conformsToCommonAPI = true; // Keep track of where we have rotate and scale Pivots and their inverse so // that we can combine them later if possible unsigned int rotPivotIdx = -1, rotPivotINVIdx = -1, scalePivotIdx = -1, scalePivotINVIdx = -1; // Check if the Maya prim inheritTransform MPlug inheritPlug = iTrans.findPlug("inheritsTransform"); if (!inheritPlug.isNull()) { if(!inheritPlug.asBool()) { usdXformable.SetResetXformStack(true); } } // inspect the translate, no suffix to be closer compatibility with common API _GatherAnimChannel(TRANSLATE, iTrans, "translate", "X", "Y", "Z", &mAnimChanList, writeAnim, false); // inspect the rotate pivot translate if (_GatherAnimChannel(TRANSLATE, iTrans, "rotatePivotTranslate", "X", "Y", "Z", &mAnimChanList, writeAnim, true)) { conformsToCommonAPI = false; } // inspect the rotate pivot bool hasRotatePivot = _GatherAnimChannel(TRANSLATE, iTrans, "rotatePivot", "X", "Y", "Z", &mAnimChanList, writeAnim, true); if (hasRotatePivot) { rotPivotIdx = mAnimChanList.size()-1; } // inspect the rotate, no suffix to be closer compatibility with common API _GatherAnimChannel(ROTATE, iTrans, "rotate", "X", "Y", "Z", &mAnimChanList, writeAnim, false); // inspect the rotateAxis/orientation if (_GatherAnimChannel(ROTATE, iTrans, "rotateAxis", "X", "Y", "Z", &mAnimChanList, writeAnim, true)) { conformsToCommonAPI = false; } // invert the rotate pivot if (hasRotatePivot) { AnimChannel chan; chan.usdOpType = UsdGeomXformOp::TypeTranslate; chan.precision = UsdGeomXformOp::PrecisionFloat; chan.opName = "rotatePivot"; chan.isInverse = true; mAnimChanList.push_back(chan); rotPivotINVIdx = mAnimChanList.size()-1; } // inspect the scale pivot translation if (_GatherAnimChannel(TRANSLATE, iTrans, "scalePivotTranslate", "X", "Y", "Z", &mAnimChanList, writeAnim, true)) { conformsToCommonAPI = false; } // inspect the scale pivot point bool hasScalePivot = _GatherAnimChannel(TRANSLATE, iTrans, "scalePivot", "X", "Y", "Z", &mAnimChanList, writeAnim, true); if (hasScalePivot) { scalePivotIdx = mAnimChanList.size()-1; } // inspect the shear. Even if we have one xform on the xform list, it represents a share so we should name it if (_GatherAnimChannel(SHEAR, iTrans, "shear", "XY", "XZ", "YZ", &mAnimChanList, writeAnim, true)) { conformsToCommonAPI = false; } // add the scale. no suffix to be closer compatibility with common API _GatherAnimChannel(SCALE, iTrans, "scale", "X", "Y", "Z", &mAnimChanList, writeAnim, false); // inverse the scale pivot point if (hasScalePivot) { AnimChannel chan; chan.usdOpType = UsdGeomXformOp::TypeTranslate; chan.precision = UsdGeomXformOp::PrecisionFloat; chan.opName = "scalePivot"; chan.isInverse = true; mAnimChanList.push_back(chan); scalePivotINVIdx = mAnimChanList.size()-1; } // If still potential common API, check if the pivots are the same and NOT animated/connected if (hasRotatePivot != hasScalePivot) { conformsToCommonAPI = false; } if (conformsToCommonAPI && hasRotatePivot && hasScalePivot) { AnimChannel rotPivChan, scalePivChan; rotPivChan = mAnimChanList[rotPivotIdx]; scalePivChan = mAnimChanList[scalePivotIdx]; // If they have different sampleType or are ANIMATED, does not conformsToCommonAPI anymore for (unsigned int i = 0;i<3;i++) { if (rotPivChan.sampleType[i] != scalePivChan.sampleType[i] || rotPivChan.sampleType[i] == ANIMATED) { conformsToCommonAPI = false; } } // If The defaultValue is not the same, does not conformsToCommonAPI anymore if (!GfIsClose(rotPivChan.defValue, scalePivChan.defValue, 1e-9)) { conformsToCommonAPI = false; } // If opType, usdType or precision are not the same, does not conformsToCommonAPI anymore if (rotPivChan.opType != scalePivChan.opType || rotPivChan.usdOpType != scalePivChan.usdOpType || rotPivChan.precision != scalePivChan.precision) { conformsToCommonAPI = false; } if (conformsToCommonAPI) { // To Merge, we first rename rotatePivot and the scalePivot inverse // to pivot. Then we remove the scalePivot and the inverse of the // rotatePivot. // // This means that pivot and its inverse will wrap rotate and scale // since no other ops have been found // // NOTE: scalePivotIdx > rotPivotINVIdx mAnimChanList[rotPivotIdx].opName = "pivot"; mAnimChanList[scalePivotINVIdx].opName = "pivot"; mAnimChanList.erase(mAnimChanList.begin()+scalePivotIdx); mAnimChanList.erase(mAnimChanList.begin()+rotPivotINVIdx); } } // Loop over anim channel vector and create corresponding XFormOps // including the inverse ones if needed TF_FOR_ALL(iter, mAnimChanList) { AnimChannel& animChan = *iter; animChan.op = usdXformable.AddXformOp( animChan.usdOpType, animChan.precision, TfToken(animChan.opName), animChan.isInverse); }