Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duik Bassel wrong hand rotation #1661

Closed
mayorandrew opened this issue Jun 4, 2019 · 9 comments
Closed

Duik Bassel wrong hand rotation #1661

mayorandrew opened this issue Jun 4, 2019 · 9 comments

Comments

@mayorandrew
Copy link

Tell us about your environment
I am running lottie-web 5.5.4 in very simple create-react-app single page web app.
Here is the complete codesandbox:
https://codesandbox.io/s/lottie-web-duik-bassel-error-k2s7t

  • Browser and Browser Version: Google Chrome 74
  • After Effects Version: 15.1.1 (Build 12)

What did you do? Please explain the steps you took before you encountered the problem.
I used Duik Bassel to make a simple humanoid character and added Walk Cycle to it.
I attached a single hand to the bones.
I exported the animation using bodymovin to v8.json (see in codesandbox).
I played this animation using the lottie-web version 5.5.4.

What did you expect to happen?
Walk Cycle uses Forward Kinematics (instead of IK) for hand wobbling, so the hand gently moves.
Hand is oriented down.
after_effects

What actually happened? Please include as much relevant detail as possible.
Hand has constant orientation (as if I disable the hand rotation expression on layer "S | Hand 2")
chrome

Arm movement is correct. (Only hand wobble is missing)

Investigation
I digged through the source code and exported v8.json, comparing it with the values I see in AE, and found out that:

Property "Rotation" of Layer "S | Hand 2" uses expression which in this case essentially boils down to:

var ctrl = effect("IK")(1);
var fx = ctrl.effect("IK | Hand")
var parentRot = parent.transform.rotation;
fx(13) - parentRot.valueAtTime(0)

which references property 13 of the effect "IK | Hand" on the layer "C | Hand 2".
That property is defined using the expression which can be simplified to:

var walkFx = thisComp.layer('C | Walk Cycle').effect('Walk Cycle');
$bm_sum(walkFx(75).value, value)

Going next to the property 75 of the effect "Walk Cycle" of the layer "C | Walk Cycle" resolves in simple negation of property 69 of the same effect. While the property 69 has some extensive computation in which it references property 68 like this:

var fx = effect('Walk Cycle');
rot = fx(68);
overlap = ...
rot = rot.valueAtTime($bm_sub(time, overlap));

Debugging this in Chrome reveals that call to the function valueAtTime is directed to the getStaticValueAtTime function which returns this.pv which equals 0.

Apparently this is a bug and valueAtTime should reference the getValueAtTime function.

At this point I came to the idea that valueAtTime for expression properties may not be supported at all, so I've decided to pause the debugging and write this issue and probably get some feedback before digging deeper.

Please provide a download link to the After Effects file that demonstrates the problem.
https://drive.google.com/file/d/1gWUJkCiilNDO0d3c4yB_K7JeI15WNYX0/view?usp=sharing

@bodymovin
Copy link
Collaborator

Hi, I'm having a problem when opening the .aep
There seem to be some layers missing "FootRoll | Foot" and "FootRoll | Foot 2" which are not allowing me to export the animation correctly.
Can you check if they are on the .aep you shared?

@mayorandrew
Copy link
Author

mayorandrew commented Jun 5, 2019

Hi! Sorry for that, I have accidentally deleted null layers while preparing project for sharing. I have fixed missing layers now and re-saved the project. To be sure, I've also confirmed that error still persists. New version is available at the same link:
https://drive.google.com/file/d/1gWUJkCiilNDO0d3c4yB_K7JeI15WNYX0/view?usp=sharing

@bodymovin
Copy link
Collaborator

Yes, indeed valueAtTime doesn't work with properties with expressions.
It's a difficult task to support since it would require to set the whole animation state to that point in time.
It's on my todo list but not sure when it will be ready.

@mayorandrew
Copy link
Author

Thank you for taking the time to look at this issue!

I think this limitation of valueAtTime deserves a mention in the docs.
Maybe here or here?

Also, in case someone stumbles upon this thread, I'd like to share a possible workaround.

Possible workaround

One can use Animation - Keyframe Assistant - Convert expression to keyframes or Easy Bake plugin in order to pre-bake expression before export, so that expression evaluation during playback will not be required.

In my particular case I've pre-baked the Rotation property of S | Hand and S | Hand 2 layers.

@bodymovin
Copy link
Collaborator

It makes sense to report the limitation somewhere. Perhaps on the wiki pages of the player. I'm more concerned about the phrasing since it fails on very specific cases where the targetted property is itself targetting other properties.
Anyway, PR are welcome :)

@mayorandrew
Copy link
Author

mayorandrew commented Jun 12, 2019

Are you saying that if I use prop.valueAtTime where prop is a "simple" expression property (that does not reference any other properties), it will work?
I didn't dig that deep, so I though that valueAtTime simply does not work for all expression properties.

@bodymovin
Copy link
Collaborator

yes, it should, but if you are saying that it always targets getStaticValueAtTime, perhaps it doesn't.
I'll have to look into it.

@mayorandrew
Copy link
Author

I was just asking to clarify if I understood correctly, so I could write that in the PR for documentation.

To be sure, I've made a simple demo for testing purposes.
And I discovered that even for the "simple" expression property valueAtTime references getStaticValueAtTime.

AE Project, Exported JSON

Demo have 5 layers, each of which is the shape layer containing a square. Squares go from left to right:

  • Layer1 has expression on transform.position:
[50, 150 + 50 * Math.sin(time * Math.PI)]
  • Layer2 has expression on transform.position which references Layer1 at the current time:
[transform.position[0], thisComp.layer("Layer1").transform.position[1]]
  • Layer3 has expression on transform.position which references Layer1 0.5s ago using valueAtTime:
[transform.position[0], thisComp.layer("Layer1").transform.position.valueAtTime(time - 0.5)[1]]
  • Layer4 has simple keyframe animation on transform.position for the first 4 seconds
  • Layer5 has expression on transform.position which references Layer4 0.5s ago using valueAtTime:
[transform.position[0], thisComp.layer("Layer4").transform.position.valueAtTime(time - 0.5)[1]]

Layers 1,2,4,5 behave in Lottie the same as in AE:

  • Layer1 oscillates on the y-axis using sin function
  • Layer2 is exactly the same as Layer1
  • Layer4 oscillates on the y-axis using keyframes
  • Layer5 behaves like Layer4 with 0.5s delay

But Layer3 stays in place instead of behaving like Layer2 with 0.5s delay.
Debugging that reveals that getValueAtTime for the Layer1's transform.position references getStaticValueAtTime.

According to my definition of the "simple" expression property in the question above, thisComp.layer("Layer1").transform.position is the "simple" expression property. It does not reference any other properties, it only depends on time using sin function.

Actually, based on this block of code I think getValutAtTime generally works only for keyframe properties. It executes during initialization for all the expression properties and sets getValueAtTime to getStaticValueAtTime.

In fact, if I add keyframes to the Layer1's transform.position property and make the square move in the opposite direction, Layer3 gets that data from getValueAtTime and moves in the opposite direction as well (with expected delay). But in AE that works differently. If you call valueAtTime inside the property expression, you'll get data from the keyframes, but if you call valueAtTime for the different property, you'll get data from the expression.

AE Project,
Exported json

I think that in the end there should be two getValueAtTime functions - one for internal use in the expression of the same property (current one) and one for external use which will resolve expression value at the given time.

@bodymovin
Copy link
Collaborator

I've checked the code and it targets getStaticValueAtTime if that property doesn't have keyframes. If it does, it targets valueAtTime.
But it doesn't support expressions in either case.
In order to make expressions working in this scenario, I'd have to set the state of the whole animation to the changed time and then run the expressions.
Not an easy thing to do, but I'd love to have this working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants