├── ResultImage.png ├── GUIImageExample.PNG ├── alignJointAxisZForward.py ├── README.md └── alignJointAxisZForward_Maya2022.py /ResultImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherry-leki/JointReorientation/HEAD/ResultImage.png -------------------------------------------------------------------------------- /GUIImageExample.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherry-leki/JointReorientation/HEAD/GUIImageExample.PNG -------------------------------------------------------------------------------- /alignJointAxisZForward.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherry-leki/JointReorientation/HEAD/alignJointAxisZForward.py -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Joint re-orientation on skinned mesh 2 | This Maya script sets the orientation of each joint to be identity while preserving the hierarchy and skinning info. 3 | We used the script to modify the orientation value of the joint to identify in the binding pose. 4 | The code is tested in Maya 2018 and 2019 5 | ![Alt text](ResultImage.png) 6 | 7 | > ### To run script 8 | > 1. Click the Script Editor button in the bottom right of the main Maya window and open "alignJointAxisZForward.py" 9 | > 2. Press Ctrl + Enter to execute the script 10 | > 3. GUI appears! 11 | 12 | > ### How to use 13 | > ![Alt text](GUIImageExample.PNG) 14 | > * #### Display local axis of joints 15 | > * If checkbox is checked, all local joint axes are displayed. 16 | > * #### Aligning the z-axis of all joints 17 | > * Input root joint: the root joint of the target skeleton 18 | > ※ You can drag and drop the joint object into the text field, but you should delete the path in the case of joint object. 19 | > e.g.) |Hips (X) → Hips (O) 20 | > * Input skin model: meshes skinned to the target skeleton 21 | > ※ You can drag and drop the skin model. 22 | > ※ If there are multiple skin models, you can put all models by separating with ',' (no blank) 23 | > e.g.) Body, Hair, Clothes (X) → Body,Hair,Clothes (O) 24 | > * Click the "Align joints" button to align the z-axis of all joints. 25 | > * #### Rotating the joint in object mode 26 | > * Input joint: the joint that you want to rotate 27 | > ※ You can drag and drop the joint object even if there is a path in the name. 28 | > e.g.) |Hips|Spine (O) 29 | > * x, y, z rotation values: how much you want to rotate the joint 30 | > * Click the "Rotate joint" button, then the selected joint only rotates according to values of rotation without modifying other things. 31 | -------------------------------------------------------------------------------- /alignJointAxisZForward_Maya2022.py: -------------------------------------------------------------------------------- 1 | import maya.cmds as cmds 2 | from functools import partial 3 | 4 | # Unbind skin (Not used) 5 | # unbindKeepHistory=True: Keep history占쏙옙 占쏙옙占쌍억옙占? weight 占쏙옙占쏙옙 占쏙옙占쏙옙磯占?. 6 | # skinning占쏙옙 풀占쏙옙占썽서 pose占쏙옙 占쌨띰옙占쏙옙占쏙옙占쏙옙占쏙옙 占쏙옙 占쌉쇽옙占쏙옙 占쏙옙占쏙옙占쏙옙占? 占십곤옙 占쏙옙占쏙옙. 7 | def unbindSkinFunc(): 8 | cmds.skinCluster('kFBXASC045modelShape', e=True, unbind=True, unbindKeepHistory=True, lw=True) 9 | #cmds.joint('LeftLeg', e=1, ch=1, oj='xyz', sao='yup') 10 | 11 | 12 | # Bind skin (Not used) 13 | # bindMethod=2: heat map 占쏙옙占쏙옙占쏙옙占? bind 14 | def bindSkinFunc(): 15 | cmds.select('kFBXASC045model', add=True) 16 | cmds.select('Hips', add=True) 17 | cmds.skinCluster(bindMethod=2) 18 | 19 | 20 | # Set moveJointsMode flag in skinCluster function 21 | # mjm = True: joints can be moved without modifying the skinning (skining占쏙옙 占쏙옙占쏙옙占쏙옙占쏙옙 占십곤옙 joint占쏙옙 占쏙옙占쏙옙 占쏙옙占쏙옙) 22 | # After modifying joints, set the mjm=False for taking the skin out of move joints mode(joint 占쏙옙占쏙옙 占식울옙 False占쏙옙 占쏙옙占쏙옙占싹깍옙) 23 | def setMoveJointsMode(skinModel, mode): 24 | #skinModel = skinModel + "Shape" 25 | cmds.skinCluster(skinModel, e=True, mjm=mode) 26 | #cmds.skinCluster('kFBXASC045modelShape', e=True, mjm=False) 27 | 28 | 29 | # Create new skeleton 30 | # parentJoint: current selected joint of the original skeleton 31 | # rootJoint: the root joint of the original skeleton (aligning占쏙옙 skeleton占쏙옙 root joint) 32 | # copyRootJoint: the root joint of the copied skeleton (占쏙옙 占쏙옙占썹를 占쏙옙占쏙옙 占쏙옙占쏙옙占쏙옙 skeleton占쏙옙 root joint) 33 | def createChildJoint(parentJoint, rootJoint, copyRootJoint) : 34 | cmds.select(rootJoint, hi=True) 35 | cmds.select(cmds.ls(parentJoint, sl=1)) 36 | 37 | for child in cmds.listRelatives(c=True): 38 | # copy position of origin joint 39 | copyPos = cmds.xform(child, q=True, t=True, ws=True) 40 | 41 | # select parent joint in copy root 42 | cmds.select(copyRootJoint, hi=True) 43 | if parentJoint != rootJoint: 44 | cmds.select(cmds.ls(parentJoint + "_copied", sl=1)) 45 | else: 46 | cmds.select(cmds.ls(copyRootJoint, sl=1)) 47 | 48 | # create child joint 49 | cmds.joint(n=child + "_copied", p=copyPos) 50 | 51 | cmds.select(rootJoint, hi=True) 52 | if cmds.listRelatives(cmds.ls(child, sl=1), c=True): 53 | createChildJoint(child, rootJoint, copyRootJoint) 54 | 55 | cmds.select(d=True) 56 | cmds.select(rootJoint, hi=True) 57 | cmds.select(cmds.ls(parentJoint, sl=1)) 58 | 59 | cmds.select(copyRootJoint) 60 | 61 | 62 | # Show local axis of all joints 63 | # showAxis: flag for showing 64 | def showAllJointLocalAxis(showAxis, *args): 65 | jointList = cmds.ls(type="joint") 66 | for jnt in jointList: 67 | cmds.setAttr(jnt + ".displayLocalAxis", showAxis) 68 | 69 | 70 | # Copy transform values of copied skeleton to original skeleton 71 | # parentJoint: current selected joint of the original skeleton 72 | # rootJoint: the root joint of the original skeleton (aligning占쏙옙 skeleton占쏙옙 root joint) 73 | # copyRootJoint: the root joint of the copied skeleton (占쏙옙 占쏙옙占썹를 占쏙옙占쏙옙 占쏙옙占쏙옙占쏙옙 skeleton占쏙옙 root joint) 74 | def copyTransformData(parentJoint, rootJoint, copyRootJoint): 75 | cmds.select(rootJoint, hi=True) 76 | cmds.select(cmds.ls(parentJoint, sl=True)) 77 | for child in cmds.listRelatives(c=True): 78 | # copy matrix of source joint 79 | cmds.select(copyRootJoint, hi=True) 80 | cmds.select(cmds.ls(child + "_copied", sl=True)) 81 | copyMat = cmds.xform(q=True, m=True, ws=True) 82 | 83 | # set the transform matrix of copied joint to target joint 84 | cmds.select(rootJoint, hi=True) 85 | cmds.select(cmds.ls(child, sl=True)) 86 | cmds.xform(m=copyMat, ws=True) 87 | # set zero rotation to target joint 88 | cmds.xform(ro=(0, 0, 0)) 89 | cmds.setAttr(cmds.ls(child, sl=True)[0] + ".rotateAxis", 0, 0, 0) 90 | cmds.setAttr(cmds.ls(child, sl=True)[0] + ".jointOrient", 0, 0, 0) 91 | 92 | if cmds.listRelatives(cmds.ls(child, sl=1), c=True): 93 | copyTransformData(child, rootJoint, copyRootJoint) 94 | 95 | 96 | # Delete the skeleton 97 | def deleteSkeleton(rootJoint): 98 | cmds.select(rootJoint, hi=True) 99 | cmds.delete() 100 | 101 | # Aligning joint function for UI 102 | def alignJointsRotAxis(rootJoint, skinModelList, *args): 103 | # Get root joint and skin model 104 | rootJoint = cmds.textField(rootJoint, q=True, tx=True) 105 | skinModelList = cmds.textField(skinModelList, q=True, tx=True) 106 | 107 | # Check inputs 108 | if not rootJoint: 109 | print("*Input the root joint") 110 | return 111 | if not skinModelList: 112 | print("*Input the skin model") 113 | return 114 | 115 | skinModelList = skinModelList.split(',') 116 | 117 | # Aligning process 118 | cmds.select(d=True) 119 | # Copy root joint 120 | rootPos = cmds.xform(rootJoint, q=True, t=True, ws=True) 121 | copyRoot = cmds.joint(n=rootJoint + "_copied", p=rootPos) 122 | copyRootName = cmds.ls(copyRoot)[0] 123 | 124 | # Call createChildJoint 125 | root = cmds.ls(rootJoint)[0] 126 | createChildJoint(root, rootJoint, copyRootName) 127 | 128 | print("=== Copying the skeleton is done ===") 129 | 130 | # Copy source skeleton transformation to target skeleton 131 | for skinModel in skinModelList: 132 | setMoveJointsMode(skinModel, True) 133 | cmds.xform(rootJoint, m=cmds.xform(copyRootName, q=True, m=True, ws=True), ws=True) 134 | cmds.xform(rootJoint, ro=(0, 0, 0)) 135 | cmds.setAttr(rootJoint + '.jointOrient', 0, 0, 0) 136 | cmds.setAttr(rootJoint + '.rotateAxis', 0, 0, 0) 137 | copyTransformData(root, rootJoint, copyRootName) 138 | for skinModel in skinModelList: 139 | setMoveJointsMode(skinModel, False) 140 | 141 | print("=== Aligning the original skeleton is done ===") 142 | 143 | # delete copied skeleton 144 | deleteSkeleton(copyRootName) 145 | 146 | 147 | # Rotate joint function in object mode 148 | def rotateJoint(selectedJoint, rotX, rotY, rotZ, *args): 149 | selectedJoint = cmds.textField(selectedJoint, q=True, tx=True) 150 | rotX = cmds.textField(rotX, q=True, tx=True) 151 | rotY = cmds.textField(rotY, q=True, tx=True) 152 | rotZ = cmds.textField(rotZ, q=True, tx=True) 153 | 154 | if not selectedJoint: 155 | print("*Input the joint name") 156 | return 157 | 158 | if not rotX or not rotY or not rotZ: 159 | print("*Input the rotate values") 160 | return 161 | 162 | cmds.selectMode(object=True) 163 | cmds.hilite(selectedJoint) 164 | cmds.select(selectedJoint + '.rotateAxis', replace=True) 165 | cmds.rotate(rotX, rotY, rotZ, relative=True, objectSpace=True, forceOrderXYZ=True) 166 | 167 | 168 | # UI for aligning joint rotation 169 | def alignmentUI(): 170 | windowName = "Joint rotation axis controller" 171 | windowSize = [310, 330] 172 | window = cmds.window(title=windowName, widthHeight=windowSize, sizeable=False) 173 | 174 | cmds.columnLayout("mainLayout") 175 | cmds.text(label=" ",h=3) 176 | 177 | # Display all local joint axis 178 | cmds.text(label="Display local axis of all joints", fn="boldLabelFont") 179 | cmds.rowLayout("displayAxesLayout", p="mainLayout", numberOfColumns=2, h=35) 180 | cmds.text(label="Display local axis ", p="displayAxesLayout") 181 | cmds.checkBox(label="", p="displayAxesLayout", 182 | onCommand=partial(showAllJointLocalAxis, True), offCommand=partial(showAllJointLocalAxis, False)) 183 | 184 | cmds.separator(w=windowSize[0], h=30, p="mainLayout") 185 | 186 | # Align all joint's z-axis to forward direction 187 | cmds.text(label="Align all joint's z-axis to forward direction", fn="boldLabelFont", p="mainLayout") 188 | cmds.text(label=" ", h=3, p="mainLayout") 189 | # Setting root joint 190 | cmds.rowLayout("rootJointLayout", p="mainLayout", numberOfColumns=2) 191 | cmds.text(label="Input root joint: ", p="rootJointLayout", w=90, align="left") 192 | rootJoint = cmds.textField(tx="Hips", p="rootJointLayout", w=150) 193 | # Setting skin model 194 | cmds.rowLayout("skinLayout", p="mainLayout", numberOfColumns=2) 195 | cmds.text(label="Input skin model: ", p="skinLayout", w=90, align="left") 196 | skinModel = cmds.textField(tx="kFBXASC045model", p="skinLayout", w=150) 197 | # Aligning button 198 | cmds.button(label="Align joints", w=windowSize[0], h=50, p="mainLayout", command=partial(alignJointsRotAxis, rootJoint, skinModel)) 199 | 200 | cmds.separator(w=windowSize[0], h=30, p="mainLayout") 201 | 202 | # Rotate certain joint 203 | cmds.text(label="Rotate the joint in object mode", fn="boldLabelFont", p="mainLayout") 204 | cmds.text(label=" ", h=3, p="mainLayout") 205 | cmds.rowLayout("selectedJointLayout", p="mainLayout", numberOfColumns=8) 206 | # Setting selected joint 207 | cmds.text(label="Input joint: ", p="selectedJointLayout") 208 | selectedJoint = cmds.textField(p="selectedJointLayout") 209 | # Setting rotate values 210 | cmds.text(label="x: ", p="selectedJointLayout", w=25, align="right") 211 | rotX = cmds.textField(tx="0", p="selectedJointLayout", w=30) 212 | cmds.text(label="y: ", p="selectedJointLayout") 213 | rotY = cmds.textField(tx="0", p="selectedJointLayout", w=30) 214 | cmds.text(label="z: ", p="selectedJointLayout") 215 | rotZ = cmds.textField(tx="0", p="selectedJointLayout", w=30) 216 | # Rotating button 217 | cmds.button(label="Rotate joint", w=windowSize[0], h=50, p="mainLayout", command=partial(rotateJoint, selectedJoint, rotX, rotY, rotZ)) 218 | 219 | cmds.showWindow(window) 220 | 221 | 222 | ''' Main ''' 223 | if __name__ == "__main__": 224 | alignmentUI() 225 | --------------------------------------------------------------------------------