[Closed] Rotate a bone in hierarhy and then rotate it back
ok… let’s do it for offset.
if you do euler rotations in order X, Y, Z to return it back you have to it in reversed order -Z, -Y, -X
if you apply rotation matrices (rotateXmatrix X) * (rotateYmatrix Y) * (rotateZmatrix Z) then you have to apply inverse matrices in reversed order inverse (rotateZmatrix Z) * inverse (rotateYmatrix Y) * inverse (rotateXmatrix X)
but in you apply a final rotation matrix TM then you just have to multiply the result with inverse TM
using matrices:
rot_tm = (eulerangles 30 40 50) as matrix3
-- rotate
$.transform = rot_tm * $.transform
-- rotate back
$.transform = (inverse rot_tm) * $.transform
understanding of eulers:
qt = eulerToQuat (eulerangles 30 40 50) order:1 --XYZ
-- (quat -0.0808047 -0.402198 -0.303372 0.860042)
inverse qt
-- (quat 0.0808047 0.402199 0.303372 0.860042)
inv_qt = eulerToQuat (eulerangles -50 -40 -30) order:6 --ZYX
-- (quat 0.0808047 0.402198 0.303372 0.860042)
thanks! seems like this is something I’m looking for.
only this code:
bnNames = #($j_shoulder_ri, $j_shoulder_le)
rot_tm = (eulerangles -20 5 25) as matrix3
for i=1 to bnNames1.count do (bnNames[i].transform = rot_tm * bnNames[i].transform)
doesn’t perform the same transformations as mine version:
bnNames = #($j_shoulder_ri, $j_shoulder_le)
for o in bnNames do o.transform = (rotateXmatrix -20) * o.transform
for o in bnNames do o.transform = (rotateYmatrix 5) * o.transform
for o in bnNames do o.transform = (rotateZmatrix 25) * o.transform
p.s.:
also maybe you help me to optimize this monster:
bnNames1 = #($j_shoulder_ri, $j_shoulder_le)
rot_tm1 = (eulerangles -20 5 25) as matrix3
bnNames2 = #($j_elbow_ri, $j_elbow_bulge_ri, $j_elbow_le, $j_elbow_bulge_le)
rot_tm2 = (eulerangles 0 0 20) as matrix3
bnNames3 = #($j_wrist_ri, $j_wrist_le)
rot_tm3 = (eulerangles 10 -8 -5) as matrix3
bnNames4 = #($j_hip_ri, $j_hip_le)
rot_tm4 = (eulerangles -7 -3 0) as matrix3
bnNames5 = #($j_ankle_ri, $j_ankle_le)
rot_tm5 = (eulerangles 3 0 0) as matrix3
bnNames6 = #($j_wrist_ri, $j_wrist_le)
rot_tm6 = (eulerangles 20 20 10) as matrix3
bnNames7 = #($j_thumb_ri_1, $j_thumb_le_1)
rot_tm7 = (eulerangles -10 -10 0) as matrix3
bnNames8 = #($j_thumb_ri_2, $j_thumb_le_2)
rot_tm8 = (eulerangles 0 0 -20) as matrix3
bnNames9 = #($j_thumb_ri_3, $j_pinky_ri_2, $j_index_ri_2, $j_thumb_le_3, $j_pinky_le_2, $j_index_le_2)
rot_tm9 = (eulerangles 0 0 -15) as matrix3
bnNames10 = #($j_pinky_ri_1, $j_pinky_le_1)
rot_tm10 = (eulerangles 0 -10 -15) as matrix3
bnNames11 = #($j_mid_ri_3, $j_mid_le_3, $j_ring_ri_3, $j_ring_le_3)
rot_tm11 = (eulerangles 0 0 -35) as matrix3
bnNames12 = #($j_mid_ri_1, $j_mid_le_1)
rot_tm12 = (eulerangles 0 -7 0) as matrix3
bnNames13 = #($j_pinky_ri_3, $j_pinky_le_3)
rot_tm13 = (eulerangles 0 0 -25) as matrix3
bnNames14 = #($j_index_ri_3, $j_index_le_3)
rot_tm14 = (eulerangles 0 0 -25) as matrix3
bnNames15 = #($j_mid_ri_2, $j_ring_ri_2, $j_mid_le_2, $j_ring_le_2)
rot_tm15 = (eulerangles 0 0 -30) as matrix3
on PPose pressed do (
for i=1 to bnNames1.count do (bnNames1[i].transform = rot_tm1 * bnNames1[i].transform)
for i=1 to bnNames2.count do (bnNames2[i].transform = rot_tm2 * bnNames2[i].transform)
for i=1 to bnNames3.count do (bnNames3[i].transform = rot_tm3 * bnNames3[i].transform)
for i=1 to bnNames4.count do (bnNames4[i].transform = rot_tm4 * bnNames4[i].transform)
for i=1 to bnNames5.count do (bnNames5[i].transform = rot_tm5 * bnNames5[i].transform)
for i=1 to bnNames6.count do (bnNames6[i].transform = rot_tm6 * bnNames6[i].transform)
for i=1 to bnNames7.count do (bnNames7[i].transform = rot_tm7 * bnNames7[i].transform)
for i=1 to bnNames8.count do (bnNames8[i].transform = rot_tm8 * bnNames8[i].transform)
for i=1 to bnNames9.count do (bnNames9[i].transform = rot_tm9 * bnNames9[i].transform)
for i=1 to bnNames10.count do (bnNames10[i].transform = rot_tm10 * bnNames10[i].transform)
for i=1 to bnNames11.count do (bnNames11[i].transform = rot_tm11 * bnNames11[i].transform)
for i=1 to bnNames12.count do (bnNames12[i].transform = rot_tm12 * bnNames12[i].transform)
for i=1 to bnNames13.count do (bnNames13[i].transform = rot_tm13 * bnNames13[i].transform)
for i=1 to bnNames14.count do (bnNames14[i].transform = rot_tm14 * bnNames14[i].transform)
for i=1 to bnNames15.count do (bnNames15[i].transform = rot_tm15 * bnNames15[i].transform)
)
on PPose rightclick do (
for i=1 to bnNames1.count do (bnNames1[i].transform = (inverse rot_tm1) * bnNames1[i].transform)
for i=1 to bnNames2.count do (bnNames2[i].transform = (inverse rot_tm2) * bnNames2[i].transform)
for i=1 to bnNames3.count do (bnNames3[i].transform = (inverse rot_tm3) * bnNames3[i].transform)
for i=1 to bnNames4.count do (bnNames4[i].transform = (inverse rot_tm4) * bnNames4[i].transform)
for i=1 to bnNames5.count do (bnNames5[i].transform = (inverse rot_tm5) * bnNames5[i].transform)
for i=1 to bnNames6.count do (bnNames6[i].transform = (inverse rot_tm6) * bnNames6[i].transform)
for i=1 to bnNames7.count do (bnNames7[i].transform = (inverse rot_tm7) * bnNames7[i].transform)
for i=1 to bnNames8.count do (bnNames8[i].transform = (inverse rot_tm8) * bnNames8[i].transform)
for i=1 to bnNames9.count do (bnNames9[i].transform = (inverse rot_tm9) * bnNames9[i].transform)
for i=1 to bnNames10.count do (bnNames10[i].transform = (inverse rot_tm10) * bnNames10[i].transform)
for i=1 to bnNames11.count do (bnNames11[i].transform = (inverse rot_tm11) * bnNames11[i].transform)
for i=1 to bnNames12.count do (bnNames12[i].transform = (inverse rot_tm12) * bnNames12[i].transform)
for i=1 to bnNames13.count do (bnNames13[i].transform = (inverse rot_tm13) * bnNames13[i].transform)
for i=1 to bnNames14.count do (bnNames14[i].transform = (inverse rot_tm14) * bnNames14[i].transform)
for i=1 to bnNames15.count do (bnNames15[i].transform = (inverse rot_tm15) * bnNames15[i].transform)
)
optimization…
first of all we have to understand that optimization equals organization.
Shorter code doesn’t mean better!
We must organize our code in such a way as not to repeat a part of the code if it is changed, and to make it simple and clear in the case of an extension.
this is how i do my tools, and how i learn my students:
global TOSYK_CharacterRig_Setup
(
struct CharacterRig_Struct
(
InitBone =
(
struct InitBone
(
private
tm,
public
name,
origin = [0,0,0],
node,
fn getNode = getnodebyname name,
fn setup = if isvalidnode node do node.transform = tm * node.transform,
fn backup = if isvalidnode node do node.transform = inverse tm * node.transform,
fn update =
(
node = getnodebyname name
tm = (eulerangles origin[1] origin[2] origin[3]) as matrix3
),
on create do
(
update()
)
)
),
BoneGroup =
(
struct BoneGroup (name, bones)
),
private
shoulder_origin = [-20,5,25],
elbow_origin = [0,0,20],
public
bone_groups =
#(
BoneGroup name:#shoulder bones:
#(
InitBone name:#j_shoulder_ri origin:shoulder_origin,
InitBone name:#j_shoulder_lo origin:shoulder_origin
),
BoneGroup name:#elbow bones:
#(
InitBone name:#j_elbow_ri origin:elbow_origin,
InitBone name:#j_elbow_bulge_ri origin:elbow_origin,
InitBone name:#j_elbow_le origin:elbow_origin,
InitBone name:#j_elbow_bulge_le origin:elbow_origin
)
-- all other bone groups ...
),
mapped fn update_bone bone = bone.update(),
mapped fn setup_bone bone = bone.setup(),
mapped fn backup_bone bone = bone.backup(),
fn update =
(
for bg in bone_groups do update_bone bg.bones
),
fn setup =
(
for bg in bone_groups do setup_bone bg.bones
),
fn backup =
(
for bg in bone_groups do backup_bone bg.bones
),
dialog =
(
rollout dialog "TOSYK Rig" width:200
(
local owner = if owner != undefined do owner
group "Edit: "
(
button pose_all_bt "Pose All Bones" width:182 align:#left offset:[-4,0] tooltip:"Setup All Bones\n RC\t- Backup"
)
button update_all_bt "Update Rig Data" width:182 align:#left offset:[-4,4] tooltip:"Update Rig Data"
on pose_all_bt pressed do undo "Setup" on
(
if isstruct owner do owner.setup()
)
on pose_all_bt rightclick do undo "Beckup" on
(
if isstruct owner do owner.backup()
)
fn makeColorByName name hash:1 =
(
maxops.colorById (getHashValue name hash) &c
c
)
group "Debug: "
(
button create_test_bt "Create Test" width:182 align:#left offset:[-4,0] tooltip:"Create Test"
)
on create_test_bt pressed do undo "Create Test" on if isstruct owner do
(
delete objects
for bg in owner.bone_groups do
(
col = makeColorByName (bg.name as string)
for node in bg.bones do
(
point name:node.name axis:on pos:(random [100,100,100] -[100,100,100]) wirecolor:col
)
)
owner.update()
)
on dialog close do
(
)
on dialog open do
(
)
)
),
fn destroy_dialog =
(
try (destroydialog ::TOSYK_CharacterRig_Setup.dialog) catch()
),
fn create_dialog =
(
destroy_dialog()
createdialog dialog
),
on create do
(
destroy_dialog()
update()
dialog.owner = this
)
)
global _cs = TOSYK_CharacterRig_Setup = CharacterRig_Struct()
ok
)
/*
_cs.create_dialog()
*/
The DEBUG part is really not needed for your final code (tool). But it’s easier to make a simple scene for debugging complex solutions. (in my case, I had nothing to debug the code, so I do a test that covers the all needs to implement the idea). You can remove or comment this part of code
Ask questions if don’t understand any of my code solutions. Of course if you want to learn instead of just use it…
It’s not about How to Make a Character Rig. It’s only about how to organize code for a tool that will grow and improve
thanks denis, really appreciate for you help!
unfortunately my knowledges are way too far from the understanding such a complex solution — right now I’m somewhere between the parsing of files and copy/paste others work into scripts of mine
it would take too much time to understand this before I can use it though I wanted a simple solution for my current needs.
it’s a matter of fact I love to organize things. but what’s more important at the moment it’s efficient using of time
speaking about the rig I must say I always wanted to add automatic rig-system and a way to use any animation on a skeleton with a different bone names but that would be offtopic I suppose