├── .gitattributes ├── .gitignore ├── AEparallelFrameTransportTemplate.mel ├── CMakeLists.txt ├── LICENSE ├── README.md ├── demo.gif ├── demo.py ├── parallelFrameTransportNode.cpp ├── parallelFrameTransportNode.sln └── parallelFrameTransportNode.vcxproj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /AEparallelFrameTransportTemplate.mel: -------------------------------------------------------------------------------- 1 | global proc AEparallelFrameTransportTemplate( string $nodeName ) 2 | { 3 | editorTemplate -beginScrollLayout; 4 | 5 | editorTemplate -beginLayout "Parallel Frame Transport" -collapse 0; 6 | 7 | editorTemplate -addControl "restLength"; 8 | editorTemplate -addControl "params"; 9 | editorTemplate -addControl "absoluteParam"; 10 | editorTemplate -addControl "startMatrix"; 11 | AEaddRampControl( $nodeName + "twist"); 12 | AEaddRampControl( $nodeName + "scaleRamp"); 13 | editorTemplate -addControl "outTranslate"; 14 | editorTemplate -addControl "outRotate"; 15 | editorTemplate -addControl "outScale"; 16 | 17 | editorTemplate -endLayout; 18 | 19 | editorTemplate -addExtraControls; 20 | editorTemplate -endScrollLayout; 21 | } 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.12) 2 | 3 | PROJECT ( FrameTransportNode ) 4 | 5 | SET(CMAKE_CXX_STANDARD 11) 6 | SET(CMAKE_CXX_EXTENSIONS OFF) 7 | 8 | 9 | FIND_PACKAGE ( Maya REQUIRED ) 10 | 11 | ADD_DEFINITIONS ( ${MAYA_DEFINITIONS} ) 12 | 13 | INCLUDE_DIRECTORIES ( ${MAYA_INCLUDE_DIR} ) 14 | 15 | ADD_LIBRARY ( parallelFrameTransportNode SHARED 16 | parallelFrameTransportNode.cpp 17 | ) 18 | 19 | TARGET_LINK_LIBRARIES ( parallelFrameTransportNode 20 | ${MAYA_Foundation_LIBRARY} 21 | ${MAYA_OpenMayaUI_LIBRARY} 22 | ${MAYA_OpenMaya_LIBRARY} 23 | ) 24 | 25 | MAYA_SET_LIBRARY_PROPERTIES ( parallelFrameTransportNode ) 26 | 27 | INSTALL ( TARGETS 28 | parallelFrameTransportNode 29 | DESTINATION 30 | ./maya${MAYA_VERSION_NUMBER}/plug-ins 31 | ) 32 | 33 | INSTALL ( FILES 34 | AEparallelFrameTransportTemplate.mel 35 | DESTINATION 36 | ./maya${MAYA_VERSION_NUMBER}/scripts 37 | ) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Paul Schweizer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # parallelFrameTransportNode 2 | 3 | ![](demo.gif) 4 | 5 | Maya Node implementing the Parallel Frame Transport algorithm by Hanson and Ma, published in this paper: https://pdfs.semanticscholar.org/7e65/2313c1f8183a0f43acce58ae8d8caf370a6b.pdf 6 | 7 | The algorithm transports an initial vector along a curve. 8 | The transported vector is then used to determine consistent rotation values for any point on the curve. 9 | 10 | ## Features 11 | The node requires a curve and an initial matrix as input and will return rotations as well as translation values for the given param values. 12 | 13 | Twisting is limitless and will never flip. It is controlled through a ramp attribute allowing for further use cases. 14 | 15 | Additionally the node calculates scale values that can be used for volume preservation. The scales can be controlled through a ramp attribute allowing for further use cases. 16 | 17 | ## How to use 18 | 1. Make **AEparallelFrameTransportTemplate.mel** available in the MAYA_SCRIPT_PATH. 19 | 2. Load the plugin. 20 | 3. Use the python code from [demo.py](demo.py) to explore the capabilities of this node 21 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaulSchweizer/parallelFrameTransportNode/4c0e027680c8be03f5c7848568e370722f6c068f/demo.gif -------------------------------------------------------------------------------- /demo.py: -------------------------------------------------------------------------------- 1 | from maya import cmds 2 | 3 | 4 | cmds.file(new=True, f=True) 5 | 6 | if cmds.pluginInfo("parallelFrameTransportNode", q=True, loaded=True): 7 | cmds.unloadPlugin("parallelFrameTransportNode") 8 | cmds.loadPlugin("parallelFrameTransportNode") 9 | 10 | 11 | pft = cmds.createNode("parallelFrameTransport", n="pft") 12 | curve = cmds.curve(p=[[0, 0, 0], [0, 2, 0], [0, 4, 0], [0, 6, 0]], d=3) 13 | start_matrix = cmds.createNode("transform", n="startMatrix") 14 | 15 | # The pft node needs an input curve and a start matrix to determine the 16 | # initial tangent and normal vector 17 | # 18 | cmds.connectAttr( 19 | "{0}.worldSpace[0]".format(curve), "{0}.inputCurve".format(pft)) 20 | cmds.connectAttr( 21 | "{0}.worldMatrix[0]".format(start_matrix), "{0}.startMatrix".format(pft)) 22 | 23 | # Create a number of joints and distribute them along the curve via pft node 24 | # 25 | num_joints = 10 26 | skin_jnts = [] 27 | for i in range(num_joints): 28 | cmds.setAttr("pft.params[{0}]".format(i), i / float(num_joints - 1)) 29 | cmds.select(d=True) 30 | joint = cmds.joint(p=[0, 0, 0]) 31 | cmds.setAttr("{0}.displayLocalAxis".format(joint), 1) 32 | cmds.connectAttr( 33 | "{0}.outTranslate[{1}]".format(pft, i), "{0}.translate".format(joint)) 34 | cmds.connectAttr( 35 | "{0}.outRotate[{1}]".format(pft, i), "{0}.rotate".format(joint)) 36 | cmds.connectAttr( 37 | "{0}.outScale[{1}]".format(pft, i), "{0}.scaleX".format(joint)) 38 | cmds.connectAttr( 39 | "{0}.outScale[{1}]".format(pft, i), "{0}.scaleZ".format(joint)) 40 | if i > 0 and i < num_joints - 1: 41 | skin_jnts.append(joint) 42 | 43 | # Create demo rig 44 | # 45 | start_ctl = cmds.circle(n="Start_CTL", nr=[0, 1, 0]) 46 | start_jnt = cmds.joint(n="Start_JNT") 47 | end_ctl = cmds.circle(n="End_CTL", nr=[0, 1, 0]) 48 | end_jnt = cmds.joint(n="End_JNT") 49 | cmds.setAttr("{0}.ty".format(end_ctl[0]), 6) 50 | cmds.setAttr("{0}.rotateOrder".format(end_ctl[0]), 1) 51 | cmds.makeIdentity(end_ctl, apply=True, t=True) 52 | 53 | # Skin the curve to the two control joints that also act as skin joints for the geo 54 | # 55 | curve_skin = cmds.skinCluster( 56 | [start_jnt, end_jnt], curve, n='curve_SKN', tsb=True)[0] 57 | 58 | # Twist setup, controlled by the start and end controller Y rotation 59 | # 60 | cmds.setAttr("{0}.twist[1].twist_Position".format(pft), 1) 61 | cmds.connectAttr("{0}.rotateY".format(start_ctl[0]), 62 | "{0}.twist[0].twist_FloatValue".format(pft)) 63 | cmds.connectAttr("{0}.rotateY".format(end_ctl[0]), 64 | "{0}.twist[1].twist_FloatValue".format(pft)) 65 | 66 | # Volume Preservation 67 | # 68 | cmds.setAttr("{0}.restLength".format(pft), 6) # hardcoded length of the curve 69 | cmds.setAttr("{0}.scaleRamp[0].scaleRamp_FloatValue".format(pft), 0) 70 | cmds.setAttr("{0}.scaleRamp[1].scaleRamp_Position".format(pft), 0.5) 71 | cmds.setAttr("{0}.scaleRamp[2].scaleRamp_Position".format(pft), 1) 72 | cmds.setAttr("{0}.scaleRamp[1].scaleRamp_FloatValue".format(pft), 2) 73 | 74 | # Create and skin test geo 75 | # 76 | cube = cmds.polyCube(h=6, sy=num_joints) 77 | cmds.setAttr("{0}.ty".format(cube[0]), 3) 78 | cmds.skinCluster( 79 | [start_jnt] + skin_jnts + [end_jnt], cube, tsb=True)[0] 80 | -------------------------------------------------------------------------------- /parallelFrameTransportNode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | class parallelFrameTransport : public MPxNode 24 | { 25 | public: 26 | parallelFrameTransport(); 27 | ~parallelFrameTransport() override; 28 | 29 | MStatus compute(const MPlug& plug, MDataBlock& data) override; 30 | 31 | static void* creator(); 32 | static MStatus initialize(); 33 | 34 | static MObject inputCurve; 35 | static MObject restLength; 36 | static MObject params; 37 | static MObject startMatrix; 38 | static MObject twist; 39 | static MObject absoluteParam; 40 | static MObject scaleRamp; 41 | 42 | static MObject outTranslate; 43 | static MObject outRotateX; 44 | static MObject outRotateY; 45 | static MObject outRotateZ; 46 | static MObject outRotate; 47 | static MObject outScale; 48 | static MTypeId id; 49 | 50 | private: 51 | static double degToRad; 52 | }; 53 | 54 | MTypeId parallelFrameTransport::id(0x00130a00); 55 | MObject parallelFrameTransport::inputCurve; 56 | MObject parallelFrameTransport::restLength; 57 | MObject parallelFrameTransport::params; 58 | MObject parallelFrameTransport::startMatrix; 59 | MObject parallelFrameTransport::twist; 60 | MObject parallelFrameTransport::absoluteParam; 61 | MObject parallelFrameTransport::scaleRamp; 62 | 63 | MObject parallelFrameTransport::outTranslate; 64 | MObject parallelFrameTransport::outRotateX; 65 | MObject parallelFrameTransport::outRotateY; 66 | MObject parallelFrameTransport::outRotateZ; 67 | MObject parallelFrameTransport::outRotate; 68 | MObject parallelFrameTransport::outScale; 69 | 70 | double parallelFrameTransport::degToRad = M_PI / 180; 71 | 72 | parallelFrameTransport::parallelFrameTransport() {} 73 | parallelFrameTransport::~parallelFrameTransport() {} 74 | 75 | void* parallelFrameTransport::creator() 76 | { 77 | return new parallelFrameTransport(); 78 | } 79 | 80 | MStatus parallelFrameTransport::compute(const MPlug& plug, MDataBlock& data) 81 | { 82 | MStatus status; 83 | 84 | if (plug == outTranslate || plug == outRotate || plug == outScale || 85 | plug.parent() == outTranslate || plug.parent() == outRotate || plug.parent() == outScale) 86 | { 87 | // Input handles 88 | MArrayDataHandle inputDataParams = data.inputValue(params, &status); 89 | CHECK_MSTATUS_AND_RETURN_IT(status); 90 | MDataHandle inputDataCurve = data.inputValue(inputCurve, &status); 91 | CHECK_MSTATUS_AND_RETURN_IT(status); 92 | MFnNurbsCurve curveFn(inputDataCurve.asNurbsCurveTransformed(), &status); 93 | CHECK_MSTATUS_AND_RETURN_IT(status); 94 | MDataHandle inputDataStartMatrix = data.inputValue(startMatrix, &status); 95 | CHECK_MSTATUS_AND_RETURN_IT(status); 96 | MRampAttribute inputDataTwist(thisMObject(), twist, &status); 97 | CHECK_MSTATUS_AND_RETURN_IT(status); 98 | MDataHandle inputDataAbsoluteParam = data.inputValue(absoluteParam, &status); 99 | CHECK_MSTATUS_AND_RETURN_IT(status); 100 | MRampAttribute inputDataScaleRamp(thisMObject(), scaleRamp, &status); 101 | CHECK_MSTATUS_AND_RETURN_IT(status); 102 | MDataHandle inputRestLength = data.inputValue(restLength, &status); 103 | CHECK_MSTATUS_AND_RETURN_IT(status); 104 | 105 | // Output handles 106 | MArrayDataHandle outTranslateHandle = data.outputValue(outTranslate, &status); 107 | CHECK_MSTATUS_AND_RETURN_IT(status); 108 | MArrayDataHandle outRotateHandle = data.outputValue(outRotate, &status); 109 | CHECK_MSTATUS_AND_RETURN_IT(status); 110 | MArrayDataHandle outScaleHandle = data.outputValue(outScale, &status); 111 | CHECK_MSTATUS_AND_RETURN_IT(status); 112 | 113 | // Vars 114 | double param; 115 | MPoint position; 116 | MDataHandle handle; 117 | MVector tangent; 118 | MQuaternion q; 119 | MVector normal; 120 | MVector binormal; 121 | double theta; 122 | MMatrix m; 123 | double rot[3]; 124 | MTransformationMatrix::RotationOrder rotOrder; 125 | MMatrix StartMatrix = inputDataStartMatrix.asMatrix(); 126 | double curveLength = curveFn.length(); 127 | bool absParam = inputDataAbsoluteParam.asBool(); 128 | float twistRampValue; 129 | float scaleRampValue; 130 | double curveRestLength = inputRestLength.asDouble(); 131 | double lengthRatio = pow(curveRestLength / curveLength, 0.5) - 1; 132 | 133 | MVector prevNormal = MVector(StartMatrix[0][0], StartMatrix[0][1], StartMatrix[0][2]); 134 | MVector prevTangent = MVector(StartMatrix[1][0], StartMatrix[1][1], StartMatrix[1][2]); 135 | 136 | for (unsigned int i = 0; i < inputDataParams.elementCount(); i++) 137 | { 138 | param = inputDataParams.outputValue().asDouble(); 139 | 140 | // Param depending on length if absoluteParam is enabled 141 | if (absParam) { 142 | param = curveFn.findParamFromLength(curveLength * param); 143 | } 144 | 145 | // Translate 146 | curveFn.getPointAtParam(param, position); 147 | handle = outTranslateHandle.outputValue(); 148 | handle.set(position.x, position.y, position.z); 149 | 150 | // Parallel Frame Transportation 151 | tangent = curveFn.tangent(param).normal(); 152 | binormal = tangent ^ prevTangent; 153 | if (binormal.length() == 0) 154 | { 155 | normal = prevNormal; 156 | } 157 | else 158 | { 159 | binormal.normalize(); 160 | theta = acos(prevTangent * tangent); 161 | q.setAxisAngle(binormal, theta); 162 | normal = q * prevNormal; 163 | } 164 | binormal = (normal ^ tangent).normal(); 165 | prevNormal = normal; 166 | prevTangent = tangent; 167 | 168 | // Construct TransformMatrix to extract rotation 169 | m[0][0] = normal.x; 170 | m[0][1] = normal.y; 171 | m[0][2] = normal.z; 172 | m[1][0] = tangent.x; 173 | m[1][1] = tangent.y; 174 | m[1][2] = tangent.z; 175 | m[2][0] = binormal.x; 176 | m[2][1] = binormal.y; 177 | m[2][2] = binormal.z; 178 | MTransformationMatrix transform = MTransformationMatrix(m); 179 | 180 | // Apply twist 181 | inputDataTwist.getValueAtPosition(param, twistRampValue, &status); 182 | CHECK_MSTATUS_AND_RETURN_IT(status); 183 | q.setAxisAngle(MVector().yAxis, twistRampValue * degToRad); 184 | transform.rotateBy(q, MSpace::kObject); 185 | 186 | // Set rotation 187 | transform.getRotation(rot, rotOrder); 188 | handle = outRotateHandle.outputValue(); 189 | handle.set(rot[0], rot[1], rot[2]); 190 | 191 | // Scale for volume preservation 192 | inputDataScaleRamp.getValueAtPosition(param, scaleRampValue, &status); 193 | CHECK_MSTATUS_AND_RETURN_IT(status); 194 | handle = outScaleHandle.outputValue(); 195 | handle.set(std::max(0.0, 1 + lengthRatio * scaleRampValue)); 196 | 197 | // Nexts 198 | if (i + 1 < inputDataParams.elementCount()) 199 | { 200 | CHECK_MSTATUS_AND_RETURN_IT(inputDataParams.next()); 201 | CHECK_MSTATUS_AND_RETURN_IT(outTranslateHandle.next()); 202 | CHECK_MSTATUS_AND_RETURN_IT(outRotateHandle.next()); 203 | CHECK_MSTATUS_AND_RETURN_IT(outScaleHandle.next()); 204 | } 205 | } 206 | data.setClean(plug); 207 | return MS::kSuccess; 208 | } 209 | else 210 | { 211 | return MS::kUnknownParameter; 212 | } 213 | } 214 | 215 | 216 | MStatus parallelFrameTransport::initialize() 217 | { 218 | MStatus status; 219 | MFnNumericAttribute nAttr; 220 | MFnUnitAttribute uAttr; 221 | MFnTypedAttribute tAttr; 222 | MFnMatrixAttribute mAttr; 223 | 224 | inputCurve = tAttr.create("inputCurve", "c", MFnData::kNurbsCurve); 225 | nAttr.setChannelBox(true); 226 | 227 | restLength = nAttr.create("restLength", "rl", MFnNumericData::kDouble, 0.0); 228 | nAttr.setChannelBox(true); 229 | 230 | params = nAttr.create("params", "p", MFnNumericData::kDouble, 0.0); 231 | nAttr.setArray(true); 232 | nAttr.setChannelBox(true); 233 | 234 | startMatrix = mAttr.create("startMatrix", "sm", MFnMatrixAttribute::kDouble); 235 | 236 | twist = MRampAttribute::createCurveRamp("twist", "t"); 237 | nAttr.setChannelBox(true); 238 | 239 | scaleRamp = MRampAttribute::createCurveRamp("scaleRamp", "sr"); 240 | nAttr.setChannelBox(true); 241 | 242 | absoluteParam = nAttr.create("absoluteParam", "a", MFnNumericData::kBoolean, true); 243 | nAttr.setChannelBox(true); 244 | 245 | outTranslate = nAttr.create("outTranslate", "ot", MFnNumericData::k3Double, 0.0); 246 | nAttr.setArray(true); 247 | nAttr.setWritable(true); 248 | 249 | outRotateX = uAttr.create("outRotateX", "rx", MFnUnitAttribute::kAngle, 0.0); 250 | outRotateY = uAttr.create("outRotateY", "ry", MFnUnitAttribute::kAngle, 0.0); 251 | outRotateZ = uAttr.create("outRotateZ", "rz", MFnUnitAttribute::kAngle, 0.0); 252 | outRotate = nAttr.create("outRotate", "r", outRotateX, outRotateY, outRotateZ); 253 | nAttr.setArray(true); 254 | nAttr.setWritable(true); 255 | 256 | outScale = nAttr.create("outScale", "os", MFnNumericData::kDouble, 0.0); 257 | nAttr.setArray(true); 258 | nAttr.setWritable(true); 259 | 260 | // Inputs 261 | status = addAttribute(inputCurve); 262 | if (!status) { status.perror("addAttribute(inputCurve)"); return status; } 263 | status = addAttribute(params); 264 | if (!status) { status.perror("addAttribute(params)"); return status; } 265 | status = addAttribute(restLength); 266 | if (!status) { status.perror("addAttribute(restLength)"); return status; } 267 | status = addAttribute(startMatrix); 268 | if (!status) { status.perror("addAttribute(startMatrix)"); return status; } 269 | status = addAttribute(twist); 270 | if (!status) { status.perror("addAttribute(twist)"); return status; } 271 | status = addAttribute(scaleRamp); 272 | if (!status) { status.perror("addAttribute(scaleRamp)"); return status; } 273 | status = addAttribute(absoluteParam); 274 | if (!status) { status.perror("addAttribute(absoluteParam)"); return status; } 275 | 276 | // Outputs 277 | status = addAttribute(outTranslate); 278 | if (!status) { status.perror("addAttribute(outTranslate)"); return status; } 279 | status = addAttribute(outRotate); 280 | if (!status) { status.perror("addAttribute(outRotate)"); return status; } 281 | status = addAttribute(outScale); 282 | if (!status) { status.perror("addAttribute(outScale)"); return status; } 283 | 284 | // Inputs affecting outputs 285 | status = attributeAffects(inputCurve, outTranslate); 286 | if (!status) { status.perror("attributeAffects(inputCurve, outTranslate)"); return status; } 287 | status = attributeAffects(params, outTranslate); 288 | if (!status) { status.perror("attributeAffects(params, outTranslate)"); return status; } 289 | status = attributeAffects(restLength, outTranslate); 290 | if (!status) { status.perror("attributeAffects(restLength, outTranslate)"); return status; } 291 | status = attributeAffects(startMatrix, outTranslate); 292 | if (!status) { status.perror("attributeAffects(startMatrix, outTranslate)"); return status; } 293 | status = attributeAffects(absoluteParam, outTranslate); 294 | if (!status) { status.perror("attributeAffects(absoluteParam, outTranslate)"); return status; } 295 | 296 | status = attributeAffects(inputCurve, outRotate); 297 | if (!status) { status.perror("attributeAffects(inputCurve, outRotate)"); return status; } 298 | status = attributeAffects(params, outRotate); 299 | if (!status) { status.perror("attributeAffects(params, outRotate)"); return status; } 300 | status = attributeAffects(restLength, outRotate); 301 | if (!status) { status.perror("attributeAffects(restLength, outRotate)"); return status; } 302 | status = attributeAffects(startMatrix, outRotate); 303 | if (!status) { status.perror("attributeAffects(startMatrix, outRotate)"); return status; } 304 | status = attributeAffects(twist, outRotate); 305 | if (!status) { status.perror("attributeAffects(twist, outRotate)"); return status; } 306 | status = attributeAffects(absoluteParam, outRotate); 307 | if (!status) { status.perror("attributeAffects(absoluteParam, outRotate)"); return status; } 308 | 309 | status = attributeAffects(inputCurve, outScale); 310 | if (!status) { status.perror("attributeAffects(inputCurve, outScale)"); return status; } 311 | status = attributeAffects(params, outScale); 312 | if (!status) { status.perror("attributeAffects(params, outScale)"); return status; } 313 | status = attributeAffects(restLength, outScale); 314 | if (!status) { status.perror("attributeAffects(restLength, outScale)"); return status; } 315 | status = attributeAffects(startMatrix, outScale); 316 | if (!status) { status.perror("attributeAffects(startMatrix, outScale)"); return status; } 317 | status = attributeAffects(absoluteParam, outScale); 318 | if (!status) { status.perror("attributeAffects(absoluteParam, outScale)"); return status; } 319 | status = attributeAffects(scaleRamp, outScale); 320 | if (!status) { status.perror("attributeAffects(scaleRamp, outScale)"); return status; } 321 | 322 | return MS::kSuccess; 323 | } 324 | 325 | MStatus initializePlugin(MObject obj) 326 | { 327 | MStatus status; 328 | MFnPlugin plugin(obj, "https://github.com/PaulSchweizer", "1.0", "Any"); 329 | 330 | status = plugin.registerNode( 331 | "parallelFrameTransport", 332 | parallelFrameTransport::id, 333 | ¶llelFrameTransport::creator, 334 | ¶llelFrameTransport::initialize); 335 | if (!status) { 336 | status.perror("registerNode"); 337 | } 338 | return status; 339 | } 340 | 341 | MStatus uninitializePlugin(MObject obj) 342 | { 343 | MStatus status; 344 | MFnPlugin plugin(obj); 345 | 346 | status = plugin.deregisterNode(parallelFrameTransport::id); 347 | if (!status) { 348 | status.perror("deregisterNode"); 349 | } 350 | return status; 351 | } 352 | -------------------------------------------------------------------------------- /parallelFrameTransportNode.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 14.00 2 | # Visual Studio 2015 3 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "parallelFrameTransportNode", "parallelFrameTransportNode.vcxproj", "{681412B0-F197-4A2F-9263-DEA2E8690146}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|x64 = Debug|x64 8 | Release|x64 = Release|x64 9 | ReleaseDebug|x64 = ReleaseDebug|x64 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {681412B0-F197-4A2F-9263-DEA2E8690146}.Debug|x64.ActiveCfg = Debug|x64 13 | {681412B0-F197-4A2F-9263-DEA2E8690146}.Debug|x64.Build.0 = Debug|x64 14 | {681412B0-F197-4A2F-9263-DEA2E8690146}.Release|x64.ActiveCfg = Release|x64 15 | {681412B0-F197-4A2F-9263-DEA2E8690146}.Release|x64.Build.0 = Release|x64 16 | {681412B0-F197-4A2F-9263-DEA2E8690146}.ReleaseDebug|x64.ActiveCfg = ReleaseDebug|x64 17 | {681412B0-F197-4A2F-9263-DEA2E8690146}.ReleaseDebug|x64.Build.0 = ReleaseDebug|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /parallelFrameTransportNode.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | ReleaseDebug 10 | x64 11 | 12 | 13 | Release 14 | x64 15 | 16 | 17 | 18 | Win32Proj 19 | {681412B0-F197-4A2F-9263-DEA2E8690146} 20 | 21 | 22 | 23 | DynamicLibrary 24 | true 25 | v140 26 | 27 | 28 | DynamicLibrary 29 | false 30 | v140 31 | 32 | 33 | DynamicLibrary 34 | false 35 | v140 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | true 55 | $(Platform)\$(Configuration)\ 56 | .mll 57 | AllRules.ruleset 58 | $(ExecutablePath) 59 | $(IncludePath) 60 | $(ReferencePath) 61 | $(LibraryPath) 62 | $(LibraryWPath) 63 | $(SourcePath) 64 | $(ExcludePath) 65 | 66 | 67 | 68 | _DEBUG;OSWin_;WIN32;_WINDOWS;_USRDLL;NT_PLUGIN;_HAS_ITERATOR_DEBUGGING=0;_SECURE_SCL=0;_SECURE_SCL_THROWS=0;_SECURE_SCL_DEPRECATE=0;_CRT_SECURE_NO_DEPRECATE;TBB_USE_DEBUG=0;__TBB_LIB_NAME=tbb.lib;Bits64_;%(PreprocessorDefinitions) 69 | .;..\..\..\include;..\..\..\..\include;%(AdditionalIncludeDirectories) 70 | MultiThreadedDebugDLL 71 | Level3 72 | ProgramDatabase 73 | true 74 | $(IntDir)$(ProjectName).pdb 75 | Disabled 76 | 77 | 78 | true 79 | Windows 80 | ..\..\..\lib;..\..\..\..\lib;%(AdditionalLibraryDirectories) 81 | OpenMaya.lib;OpenMayaUI.lib;Foundation.lib;opengl32.lib;%(AdditionalDependencies) 82 | false 83 | $(OutDir)$(TargetName).lib 84 | 85 | 86 | copy /Y "$(SolutionDir)x64\Debug\parallelFrameTransportNode.mll" "C:\Users\Paul\Documents\maya\2018\plug-ins\parallelFrameTransportNode.mll" 87 | 88 | 89 | 90 | false 91 | $(Platform)\$(Configuration)\ 92 | .mll 93 | AllRules.ruleset 94 | 95 | 96 | 97 | NDEBUG;OSWin_;WIN32;_WINDOWS;_USRDLL;NT_PLUGIN;_HAS_ITERATOR_DEBUGGING=0;_SECURE_SCL=0;_SECURE_SCL_THROWS=0;_SECURE_SCL_DEPRECATE=0;_CRT_SECURE_NO_DEPRECATE;TBB_USE_DEBUG=0;__TBB_LIB_NAME=tbb.lib;Bits64_;%(PreprocessorDefinitions) 98 | .;..\..\..\include;..\..\..\..\include;%(AdditionalIncludeDirectories) 99 | MultiThreadedDLL 100 | Level3 101 | ProgramDatabase 102 | true 103 | $(IntDir)$(ProjectName).pdb 104 | OnlyExplicitInline 105 | true 106 | true 107 | 108 | 109 | true 110 | Windows 111 | ..\..\..\lib;..\..\..\..\lib;%(AdditionalLibraryDirectories) 112 | OpenMaya.lib;Foundation.lib;%(AdditionalDependencies) 113 | false 114 | $(OutDir)$(TargetName).lib 115 | true 116 | true 117 | 118 | 119 | 120 | false 121 | $(Platform)\$(Configuration)\ 122 | .mll 123 | AllRules.ruleset 124 | 125 | 126 | 127 | NDEBUG;OSWin_;WIN32;_WINDOWS;_USRDLL;NT_PLUGIN;_HAS_ITERATOR_DEBUGGING=0;_SECURE_SCL=0;_SECURE_SCL_THROWS=0;_SECURE_SCL_DEPRECATE=0;_CRT_SECURE_NO_DEPRECATE;TBB_USE_DEBUG=0;__TBB_LIB_NAME=tbb.lib;Bits64_;%(PreprocessorDefinitions) 128 | .;..\..\..\include;..\..\..\..\include;%(AdditionalIncludeDirectories) 129 | MultiThreadedDLL 130 | Level3 131 | ProgramDatabase 132 | true 133 | $(IntDir)$(ProjectName).pdb 134 | OnlyExplicitInline 135 | true 136 | true 137 | 138 | 139 | false 140 | Windows 141 | ..\..\..\lib;..\..\..\..\lib;%(AdditionalLibraryDirectories) 142 | OpenMaya.lib;Foundation.lib;%(AdditionalDependencies) 143 | false 144 | $(OutDir)$(TargetName).lib 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | --------------------------------------------------------------------------------