|
| 1 | +# GETOOLS is under the terms of the MIT License |
| 2 | +# Copyright (c) 2018-2024 Eugene Gataulin (GenEugene). All Rights Reserved. |
| 3 | + |
| 4 | +# Permission is hereby granted, free of charge, to any person obtaining a copy |
| 5 | +# of this software and associated documentation files (the "Software"), to deal |
| 6 | +# in the Software without restriction, including without limitation the rights |
| 7 | +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 8 | +# copies of the Software, and to permit persons to whom the Software is |
| 9 | +# furnished to do so, subject to the following conditions: |
| 10 | + |
| 11 | +# The above copyright notice and this permission notice shall be included in all |
| 12 | +# copies or substantial portions of the Software. |
| 13 | + |
| 14 | +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 15 | +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 16 | +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 17 | +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 18 | +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 19 | +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 20 | + |
| 21 | +# Author: Eugene Gataulin [email protected] https://www.linkedin.com/in/geneugene |
| 22 | +# Source code: https://github.com/GenEugene/GETools or https://app.gumroad.com/geneugene |
| 23 | + |
| 24 | +import maya.cmds as cmds |
| 25 | + |
| 26 | +from ..utils import Selector |
| 27 | +from ..utils import Text |
| 28 | + |
| 29 | + |
| 30 | +_locatorSize = 100 |
| 31 | +_nameGroupMain = "grpChain_" |
| 32 | +_nameLocatorPrefix = "loc_" |
| 33 | + |
| 34 | + |
| 35 | +def CreateRigVariant1(locatorSize=_locatorSize, *args): |
| 36 | + # Check selected objects |
| 37 | + selectedList = Selector.MultipleObjects(minimalCount = 1) |
| 38 | + if (selectedList == None): |
| 39 | + return |
| 40 | + |
| 41 | + timeCurrent = cmds.currentTime(query = True) |
| 42 | + timeMin = cmds.playbackOptions(query = True, min = True) |
| 43 | + timeMax = cmds.playbackOptions(query = True, max = True) |
| 44 | + cmds.currentTime(timeMin, edit = True, update = True) |
| 45 | + |
| 46 | + ### Create a list of names from selected objects |
| 47 | + selected = cmds.ls(selection = True) |
| 48 | + |
| 49 | + ### Create main group as a container for all new objects |
| 50 | + mainGroup = cmds.group(name = Text.SetUniqueFromText(_nameGroupMain + selected[-1]), empty = True) |
| 51 | + |
| 52 | + ### Init empty lists for groups and locators |
| 53 | + locators = [] |
| 54 | + constraintsForBake = [] |
| 55 | + |
| 56 | + ### Count of selected objects |
| 57 | + count = len(selected) |
| 58 | + |
| 59 | + ### Loop through each selected object, create groups, locators and parent them |
| 60 | + for i in range(count): |
| 61 | + ### Create locator # TODO nurbs circle as control instead of locator (optional) |
| 62 | + locator = cmds.spaceLocator(name = Text.SetUniqueFromText(_nameLocatorPrefix + selected[i]))[0] |
| 63 | + locators.append(locator) |
| 64 | + cmds.setAttr(locator + "Shape.localScaleX", locatorSize) |
| 65 | + cmds.setAttr(locator + "Shape.localScaleY", locatorSize) |
| 66 | + cmds.setAttr(locator + "Shape.localScaleZ", locatorSize) |
| 67 | + |
| 68 | + ### Parent locator to group |
| 69 | + cmds.parent(locator, mainGroup) |
| 70 | + |
| 71 | + ### Match group position and rotation |
| 72 | + cmds.matchTransform(locator, selected[i], position = True, rotation = True, scale = False) |
| 73 | + |
| 74 | + ### Parent constraint groupFixed to original object |
| 75 | + constraint = cmds.parentConstraint(selected[i], locator, maintainOffset = False) |
| 76 | + constraintsForBake.append(constraint[0]) |
| 77 | + |
| 78 | + ### Bake animation to locators and delete constraints |
| 79 | + cmds.bakeResults(locators, time = (timeMin, timeMax), simulation = True, minimizeRotation = True) |
| 80 | + cmds.delete(constraintsForBake) |
| 81 | + |
| 82 | + ### Constrain |
| 83 | + for i in range(count): |
| 84 | + cmds.pointConstraint(selected[i], locators[i], maintainOffset = False) |
| 85 | + cmds.orientConstraint(locators[i], selected[i], maintainOffset = False) |
| 86 | + |
| 87 | + if (i > 0 and i < count - 1): |
| 88 | + cmds.orientConstraint(locators[0], locators[i], maintainOffset = True) |
| 89 | + cmds.orientConstraint(locators[i + 1], locators[i], maintainOffset = True) |
| 90 | + |
| 91 | + ### Select last locator |
| 92 | + cmds.select(locators[-1], replace = True) |
| 93 | + cmds.currentTime(timeCurrent, edit = True, update = True) |
| 94 | + |
| 95 | +def CreateRigVariant2(locatorSize=_locatorSize, *args): |
| 96 | + # Check selected objects |
| 97 | + selectedList = Selector.MultipleObjects(minimalCount = 1) |
| 98 | + if (selectedList == None): |
| 99 | + return |
| 100 | + |
| 101 | + ### Objects names |
| 102 | + nameGroupFixedPrefix = "grpFixed_" |
| 103 | + nameGroupDistributedPrefix = "grpDistr_" |
| 104 | + ### Attributes names |
| 105 | + nameAttributeWeight = "distribution" |
| 106 | + nameAttributeGlobal = "global" |
| 107 | + ### Nodes names |
| 108 | + nameMultiplyDivide = "gtMultiplyDivide" |
| 109 | + |
| 110 | + ### Create a list of names from selected objects |
| 111 | + selected = cmds.ls(selection = True) |
| 112 | + |
| 113 | + ### Create main group as a container for all new objects |
| 114 | + mainGroup = cmds.group(name = Text.SetUniqueFromText(_nameGroupMain + selected[-1]), empty = True) |
| 115 | + |
| 116 | + ### Init empty lists for groups and locators |
| 117 | + groupsFixed = [] |
| 118 | + groupsDistributed = [] |
| 119 | + locators = [] |
| 120 | + constraintsForBake = [] |
| 121 | + |
| 122 | + ### Count of selected objects |
| 123 | + count = len(selected) |
| 124 | + |
| 125 | + ### Loop through each selected object, create groups, locators and parent them |
| 126 | + for i in range(count): |
| 127 | + ### Create fixed group |
| 128 | + groupFixed = cmds.group(name = Text.SetUniqueFromText(nameGroupFixedPrefix + selected[i]), empty = True) |
| 129 | + groupsFixed.append(groupFixed) |
| 130 | + |
| 131 | + ### Create distribution group |
| 132 | + groupDistributed = cmds.group(name = Text.SetUniqueFromText(nameGroupDistributedPrefix + selected[i]), empty = True) |
| 133 | + groupsDistributed.append(groupDistributed) |
| 134 | + |
| 135 | + ### Create locator # TODO use nurbs circle [circle -c 0 0 0 -nr 0 1 0 -sw 360 -r 1 -d 3 -ut 0 -tol 1e-05 -s 8 -ch 1; objectMoveCommand;] |
| 136 | + locator = cmds.spaceLocator(name = Text.SetUniqueFromText(_nameLocatorPrefix + selected[i]))[0] |
| 137 | + locators.append(locator) |
| 138 | + cmds.setAttr(locator + "Shape.localScaleX", locatorSize) |
| 139 | + cmds.setAttr(locator + "Shape.localScaleY", locatorSize) |
| 140 | + cmds.setAttr(locator + "Shape.localScaleZ", locatorSize) |
| 141 | + |
| 142 | + ### Parent locator to group |
| 143 | + cmds.parent(locator, groupDistributed) |
| 144 | + |
| 145 | + ### Parent group to corresponding hierarchy object |
| 146 | + if i == 0: |
| 147 | + cmds.parent(groupFixed, mainGroup) |
| 148 | + else: |
| 149 | + cmds.parent(groupFixed, locators[i - 1]) |
| 150 | + cmds.parent(groupDistributed, groupFixed) |
| 151 | + |
| 152 | + ### Match group position and rotation |
| 153 | + cmds.matchTransform(groupFixed, selected[i], position = True, rotation = True, scale = False) |
| 154 | + |
| 155 | + ### Parent constraint groupFixed to original object |
| 156 | + constraint = cmds.parentConstraint(selected[i], groupFixed, maintainOffset = True) |
| 157 | + constraintsForBake.append(constraint[0]) |
| 158 | + |
| 159 | + ### Bake animation to locators and delete constraints |
| 160 | + timeMin = cmds.playbackOptions(query = True, min = True) |
| 161 | + timeMax = cmds.playbackOptions(query = True, max = True) |
| 162 | + cmds.bakeResults(groupsFixed, time = (timeMin, timeMax), simulation = True, minimizeRotation = True) |
| 163 | + cmds.delete(constraintsForBake) |
| 164 | + |
| 165 | + ### Parent constraint original objects to locators |
| 166 | + for i in range(count): |
| 167 | + cmds.parentConstraint(locators[i], selected[i], maintainOffset = True) |
| 168 | + |
| 169 | + ### Show last locator Rotate Order and connect it to Distribution groups |
| 170 | + cmds.setAttr(locators[-1] + ".rotateOrder", channelBox = True) |
| 171 | + for i in range(count): |
| 172 | + groupsDistributed[i] |
| 173 | + cmds.connectAttr(locators[-1] + ".rotateOrder", groupsDistributed[i] + ".rotateOrder") |
| 174 | + |
| 175 | + ### Check if selected count less than 3 objects and break function |
| 176 | + if (count < 3): |
| 177 | + cmds.warning("You have less than 3 objects selected. Rotation distribution will not be created") |
| 178 | + return |
| 179 | + |
| 180 | + ### Create weight attribute on last locator |
| 181 | + cmds.addAttr(locators[-1], longName = nameAttributeWeight, attributeType = "double", defaultValue = count - 1) |
| 182 | + cmds.setAttr(locators[-1] + "." + nameAttributeWeight, edit = True, keyable = True) |
| 183 | + |
| 184 | + ### Create MultiplyDivide node |
| 185 | + nodeMultiplyDivide = cmds.createNode("multiplyDivide", name = Text.SetUniqueFromText(nameMultiplyDivide)) |
| 186 | + cmds.setAttr(nodeMultiplyDivide + ".operation", 2) |
| 187 | + |
| 188 | + ### Connect rotation and weight to MultiplyDivide node |
| 189 | + cmds.connectAttr(locators[-1] + ".rotate", nodeMultiplyDivide + ".input1") |
| 190 | + cmds.connectAttr(locators[-1] + "." + nameAttributeWeight, nodeMultiplyDivide + ".input2X") |
| 191 | + cmds.connectAttr(locators[-1] + "." + nameAttributeWeight, nodeMultiplyDivide + ".input2Y") |
| 192 | + cmds.connectAttr(locators[-1] + "." + nameAttributeWeight, nodeMultiplyDivide + ".input2Z") |
| 193 | + |
| 194 | + ### Connect rotation distribution to other locators' groups |
| 195 | + for i in range(1, count - 1): |
| 196 | + cmds.connectAttr(nodeMultiplyDivide + ".output", groupsDistributed[i] + ".rotate") |
| 197 | + |
| 198 | + ### Add global attribute for last locator |
| 199 | + cmds.addAttr(locators[-1], longName = nameAttributeGlobal, attributeType = "double", defaultValue = 0, minValue = 0, maxValue = 1) |
| 200 | + cmds.setAttr(locators[-1] + "." + nameAttributeGlobal, edit = True, keyable = True) |
| 201 | + |
| 202 | + ### Create Orient Constraint for last locator |
| 203 | + cmds.orientConstraint(mainGroup, groupsDistributed[-1], maintainOffset = True)[0] |
| 204 | + |
| 205 | + ### Show blend orient attribute by setting keys on constrained rotation attributes # I frankly don't know how to do it better |
| 206 | + cmds.setKeyframe(groupsDistributed[-1] + ".rx") |
| 207 | + cmds.setKeyframe(groupsDistributed[-1] + ".ry") |
| 208 | + cmds.setKeyframe(groupsDistributed[-1] + ".rz") |
| 209 | + |
| 210 | + ### Connect Global attribute to blend orient attribute |
| 211 | + cmds.connectAttr(locators[-1] + "." + nameAttributeGlobal, groupsDistributed[-1] + ".blendOrient1") |
| 212 | + |
| 213 | + ### Select last locator |
| 214 | + cmds.select(locators[-1], replace = True) |
| 215 | + |
0 commit comments