Tuesday, October 9, 2018

Frame-by-framing in YouTube and Vimeo!

On Vimeo:
Shift + Left Arrow
Shift + Right Arrow

On YouTube:
,
.


Sources:
https://motionarray.com/blog/learn-how-to-go-frame-by-frame-on-a-vimeo-video
https://www.quora.com/How-do-you-watch-YouTube-videos-frame-by-frame

Friday, October 5, 2018

IK FK snapping!

Currently having trouble snapping an IK setup to FK. I'm working with the rigger on this (BIG advantage).

The setup:
IK Animation Controls  are usually offset a bit from the Joint they drive, either in Translation, Rotation, and sometimes both (rare case). This is so that animators have controls that behave appropriately in world-space.

The problem:
Each IK control SHOULD match to the Joint that is being driven, but because of the offset, snapping can be difficult.

The solution:
Get the offset values when the rig is at its default pose during the rig-creation process! This value can be used to snap the Control to the Joint, then Constrained with the recorded offset, baked, and now you have the IK with the exact animation pose as the FK control!

How it works:
When the rigger creates a new Control and places it accordingly in relation to the Joint, there is a value (known by the rigger).

MATH!!!

So, the actual math is a bit beyond me, but the logic is there. The idea is simple.

On the rigging end:
- Get the Joint's world Transformation values, inverse it (JTI)
- Get the Control's world Transformation values (CT)
- Multiply the CT x JTI (the order is important when doing Matrix Math)
- Store the output in the Joint itself as an attribute
Sample code:
stored_values = control.worldMatrix.get() * joint.worldMatrix.get().inverse()
joint.ikOffset.set(str(stored_values))


On the animation tool end:
- Get the current world-location of the Joint (J)
- Get the offset position relative to the Joint, stored in the attribute (O)
- Multiply O x J
- Apply the new world-Transformation (translation, rotation, or both) values to the Control!
Sample code:
matrix_offset = pm.dt.Matrix(list(eval(stored_values_string)))

new_matrix = matrix_offset * joint.worldMatrix.get()
control.setTranslation(new_matrix.translate,space='world')
control.setRotation(new_matrix.rotate,space='world')


Here's what the full code would look like using Pymel!

import pymel.all as pm
joint,control = pm.selected() # Get the Joint and Control

# Multiply the Control's worldMatrix times the Joint's Inverse worldMatrix to get the offset
stored_values = control.worldMatrix.get() * joint.worldMatrix.get().inverse()
joint.ikOffset.set(str(stored_values))

# Store the value on the Joint itself as "ikOffset"
stored_values_string = joint.ikOffset.get() # For this exercise, I'll use the value below instead as an example
stored_values_string = '[[0.279186612049, 0.96009314151, -0.0166131056216, 0.0], [-0.835911951206, 0.251517268104, 0.487842468094, 0.0], [0.472552690695, -0.122311992345, 0.872773585213, 0.0], [-5.74275377315, 1.40332190313e-12, -14.0587247215, 1.0]]'
matrix_offset = pm.dt.Matrix(list(eval(stored_values_string)))

new_matrix = matrix_offset * joint.worldMatrix.get()
control.setTranslation(new_matrix.translate,space='world')
control.setRotation(new_matrix.rotate,space='world')





Sources:
Convert string to list
http://forums.devshed.com/python-programming-11/convert-string-list-71857.html

Math is Linear Algebra. Someone taught me how it works, so I'm still looking for a good tutorial on it!