Notifications
Clear all

[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

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

but if you are interested, i can give some recommendations how to do a rig as well…

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

Page 2 / 2