├── 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 | 
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 | > 
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 |
--------------------------------------------------------------------------------