-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Add support for explicit control over model animations #9339
Conversation
Thank you so much for the pull request @markw65! I noticed this is your first pull request and I wanted to say welcome to the Cesium community! The Pull Request Guidelines is a handy reference for making sure your PR gets accepted quickly, so make sure to skim that.
Reviewers, don't forget to make sure that:
|
dd84add
to
4277a6b
Compare
4277a6b
to
d7297b9
Compare
bc967f3
to
fe7cf31
Compare
@OmarShehata maybe you could take a look? |
Thanks again for your contribution @markw65! No one has commented on this pull request in 30 days. Maintainers, can you review, merge or close to keep things tidy? I'm going to re-bump this in 30 days. If you'd like me to stop, just comment with |
Thanks again for your contribution @markw65! No one has commented on this pull request in 90 days. Maintainers, can you review, merge or close to keep things tidy? I'm going to re-bump this in 90 days. If you'd like me to stop, just comment with |
1 similar comment
Thanks again for your contribution @markw65! No one has commented on this pull request in 90 days. Maintainers, can you review, merge or close to keep things tidy? I'm going to re-bump this in 90 days. If you'd like me to stop, just comment with |
78b31b0
to
7bb0d56
Compare
@ebogo1 any chance you could look at this, or find someone who could look at it? I've tried pinging people who seemed likely to be interested, and posting in the forums (https://community.cesium.com/t/position-based-control-over-animations/11558 and then https://community.cesium.com/t/how-do-i-champion-my-pull-request/12286), but no response. |
@markw65 apologies for the delay - I'm currently trying to get a few things wrapped up for the upcoming release but I'll try to get back to this as soon as possible. Thanks! |
Thanks again for your contribution @markw65! No one has commented on this pull request in 90 days. Maintainers, can you review, merge or close to keep things tidy? I'm going to re-bump this in 90 days. If you'd like me to stop, just comment with |
1 similar comment
Thanks again for your contribution @markw65! No one has commented on this pull request in 90 days. Maintainers, can you review, merge or close to keep things tidy? I'm going to re-bump this in 90 days. If you'd like me to stop, just comment with |
I'm still very interested in getting this merged. I simply have no idea how to "champion" it. Nobody responds to anything I say about it. I'll rebase again, just in case... |
7bb0d56
to
bb93f6c
Compare
Hi @markw65 - apologies for the long wait and thanks for keeping the PR up to date. I think adding some kind of manual control over "local time" is a fair extension for animations but the current implementation seems a bit restrictive to me. I could be misunderstanding, but to me it looks like the design around the new |
Hi @markw65, I noticed this while working on bringing animations to I feel like the implementation can be reworked somehow. It would be nice to have animations be independent of the scene frame's time, that's for sure. But, at least to me, it doesn't feel right that the user would have to manually increment some value and directly set it in the animation every frame. I'm not sure what would be better API design here, to be honest. My thoughts aren't very refined, but this is what I'm thinking:
These are just thoughts, not concrete suggestions, so don't take this as a call to implement them right away. I think having input from other community members and developers would help us figure out where to go from here. |
Thanks for the feedback!
I don't really follow? If there are multiple models, they will presumably be moving separately, and will consequently need to have their animationTimes set separately? eg this example
|
I like the idea of turning the new As a note, I believe Three.js and Babylon.js both expose a time multiplier as the main way to control animation speed. |
So I agree that multiplier is useful - I'm even using it in the project that uses this patch. Essentially, I use it as a scale factor so that each model would move by My claim is that if you want non-time based control over the animation, then multiplier is a very awkward way to go; and the current implementation of multiplier simply doesn't work because every time you change it, it causes a jump in where the animation is. eg suppose multiplier is 1, and you're halfway through the animation cycle, and then you change [Edit] I've thought about this more, and I think I can explain my objection to The multiplier approach is trying to recreate a function by integrating its derivative. The problem with that is that this will always introduce an "arbitrary constant" (which manifests as _offset above). To make this concrete, take my model. After it has rolled a certain distance, there is a specific point in its animation cycle that it should be at. So eg if it covers 10m per animation cycle, after going 45m, it should be exactly half way through. If you run the clock from the start, and set multiplier appropriately at each tick (and have some mechanism that adjusts In other words, multiplier is an ok way to control the animation incrementally along the path, but no good at all if you're jumping around. |
Just wanted to clarify, because as I understand it, thats not the current behavior. It uses the simulator time, not real time. |
@markw65 -- You're right, I misspoke. Thanks for the catch! |
Ok, I've updated the branch. I've added it as a patch on top of the old one, but I think it's probably better to just flatten it; its got more in common with the original code than with my first attempt. Let me know what you'd prefer. I got rid of manualAnimation, and replaced it with animateWhilePaused. The basic reason for the flag is the same; when its false, we can skip the entire update loop when the scene time hasn't changed (as the current code does). But this time, you only need to set it if you want to provide an animation function that animates while scene time is paused. So unlike the manualAnimation flag, most uses of animationTime won't need to set it (so eg my own use case, although its distance, rather than time based, doesn't need the flag because the distance can't change unless the time does). I'm not too familiar with the comment based typing scheme; but I think I eventually got it right(?), because my own project is written in typescript, and kept complaining until I got all the comments in place... |
60bb5cf
to
234151b
Compare
I think the build failure was a travis glitch (makeZipFile seems to have timed out). I've rebased the branch to get another build going. |
234151b
to
06dc7bc
Compare
No, it was a prettier check on my sandcastle example. Should be fixed now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @markw65,
Thanks for the quick updates! I wasn't able to completely review the new changes, but I wanted to give it at least a quick look. Here's a couple suggestions regarding documentation / code style; next week we can take a closer look at the code to make sure it's robust.
Source/Scene/ModelAnimation.js
Outdated
* @callback ModelAnimation.AnimationTime | ||
* | ||
* @param {Number} duration The animation's duration in seconds. | ||
* @returns {Number} The fractional part of the return value gives |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This @returns
comment seems a bit complicated, but I have to think about how to best rewrite / summarize this. Will come back to it later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this part can be simplified to
@returns {Number} Returns the local animation time.
The loop behaviors are handled automatically by ModelAnimationCollection.update
and I don't think users need to think hard about what it's doing in order to write their own animationTime
callbacks. If developers are more concerned about this they can infer it from the math in ModelAnimationCollection.update
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@markw65 Thanks for your patience. I was able to go more thoroughly through the PR and I left some feedback.
Overall the code looks good. I left a handful of suggestions for readability and documentation. This way if we have to return to the code in the future, the logic is more clearly documented.
Also, I should point out that we plan to replace the old Model
class with ModelExperimental
, sometime this year. I added animations to ModelExperimental
in #10314. We should preserve your changes by adding these to ModelExperimentalAnimationCollection
and ModelExperimentalAnimation
as well. Do you have the bandwidth to do this? The architecture and update
code is almost the same, though feel free to ask any questions along the way.
Source/Scene/ModelAnimation.js
Outdated
* @callback ModelAnimation.AnimationTime | ||
* | ||
* @param {Number} duration The animation's duration in seconds. | ||
* @returns {Number} The fractional part of the return value gives |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this part can be simplified to
@returns {Number} Returns the local animation time.
The loop behaviors are handled automatically by ModelAnimationCollection.update
and I don't think users need to think hard about what it's doing in order to write their own animationTime
callbacks. If developers are more concerned about this they can infer it from the math in ModelAnimationCollection.update
.
I think the latest update covers all the feedback/requested changes, except for updating ModelExperimental. I'll take a look at applying the changes there too. |
I've added the animationTime changes to ModelExperimentalAnimation and ModelExperimentalAnimationCollection, added the tests to ModelExperimentalAnimationCollectionSpec.js, and added a second copy of the sandcastle example that uses ModelExperimental. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @markw65,
This PR is looking really close, thanks for the quick changes! I left another round of suggestions. Once those are done, I'll do a final pass and look to merge this with main.
@@ -0,0 +1,167 @@ | |||
<!DOCTYPE html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's okay to just have the "Manually Controlled ModelAnimation" sandcastle, since the functionality should be the same between Model
and ModelExperimental
. Let's remove this and the corresponding screenshot, and squash the commit history.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
One thing I noticed is that Model.fromGltf takes a "url" option, while ModelExperimental.fromGltf takes a "gltf" option, so (at least on that one point) they're not drop-in compatible. Is that going to be fixed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point... we might want to add a url
option for compatibility, then deprecate it after a few months. I'll make a note of it for the future
@@ -0,0 +1,167 @@ | |||
<!DOCTYPE html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we name this sandcastle "Manually Controlled Animation" instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do
CHANGES.md
Outdated
@@ -10,6 +10,10 @@ | |||
|
|||
- Fixed the inaccurate computation of bounding spheres for `ModelExperimental`. [#10339](https://github.com/CesiumGS/cesium/pull/10339/) | |||
|
|||
##### Additions :tada: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We usually put Additions
above the Fixes
section
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
On an unrelated point, I just noticed the fix right above my additions:
- Fixed the inaccurate computation of bounding spheres for `ModelExperimental`. [#10339](https://github.com/CesiumGS/cesium/pull/10339/)
I noticed with my Elliptigo model that parts of it (using Model, not ModelExperimental) would randomly disappear as I zoomed in and out, and eventually decided it was down to incorrect bounding spheres (this was a year or so ago, I've forgotten the details), and found that setting "cull" to false fixed the problem.
I was never quite sure if it was a problem with my gltf, or a problem with Model itself... but did note that other gltf renderers didn't seem to have the problem (although, they may have been doing the equivalent of cull=false by default).
Looking at that pull request though, it suggests that the problem was only with ModelExperimental, and not with Model... are there any known bounding sphere issues with Model?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I'm not sure if there are any bounding sphere issues with Model
. Does the same bug happen when you render the model with ModelExperimental
?
* If this is defined, it will be used to compute the local animation time | ||
* instead of the scene's time. | ||
* | ||
* @type {ModelAnimation.AnimationTimeCallback} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make this ModelExperimentalAnimation.AnimationTimeCallback
because it's ModelExperimental
Source/Scene/ModelExperimental/ModelExperimentalAnimationCollection.js
Outdated
Show resolved
Hide resolved
new Date("January 1, 2014 12:00:00 UTC") | ||
); | ||
const animationCollection = model.activeAnimations; | ||
animationCollection.animateWhilePaused = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this line for the ModelExperimental
spec. The models get deleted after every test here (which is not true for Model
) so after creation, this will already default to false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This made me realize that I should set animateWhilePaused to false in ModelSpec.js at the end of "animates while paused with an explicit animation time".
So I did, and it broke "animates with a multiplier". The reason being that the first render in "animates with a multiplier" uses the same scene time as all the renders in "animates while paused with an explicit animation time", so the first render relied on having animateWhilePaused set, and after fixing that it took the _previousTime shortcut. So I also changed the render time for "animates while paused with an explicit animation time".
So yes, making each test start fresh is a much better design decision...
Specs/Scene/ModelExperimental/ModelExperimentalAnimationCollectionSpec.js
Show resolved
Hide resolved
Specs/Scene/ModelExperimental/ModelExperimentalAnimationCollectionSpec.js
Show resolved
Hide resolved
CHANGES.md
Outdated
@@ -10,6 +10,10 @@ | |||
|
|||
- Fixed the inaccurate computation of bounding spheres for `ModelExperimental`. [#10339](https://github.com/CesiumGS/cesium/pull/10339/) | |||
|
|||
##### Additions :tada: | |||
|
|||
- Added `ModelAnimationCollection.animateWhilePaused` and `ModelAnimation.animationTime` to allow explicit control over a model's animations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also can we put the pull request number after the text, similar to other entries in the changelog?
@markw65 let me know when this is ready for a final review! |
I think it's ready. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good! 😄 Just a few tweaks. Once these and the merge conflict in CHANGES.md
are resolved, I'll go ahead with the merge to main.
Specs/Scene/ModelExperimental/ModelExperimentalAnimationCollectionSpec.js
Outdated
Show resolved
Hide resolved
Cesium currently only supports time based animation. This can be inconvenient if the phase of the animation is related to something other than time (eg distance along a path of an object moving at a variable speed). This came up before in CesiumGS#7361, but the author was persuaded that it was better to use nodeTransformations to explicitly control the model. That was (just) doable with that example, because there were just 3 pairs of wheels, all of which needed the exact same, relatively trivial, transformations. The proposed solution was also cumbersome, relying on modifying `multiplier` on the fly, with the downside that modifying multiplier also reset the phase of the animation. For more complex models, with less uniform animations, this approach isn't really doable - especially if you want the same code to work for multiple models. This adds an animationTime function to ModelAnimation. If set, it's used by ModelAnimationCollection.update to compute the localAnimationTime, rather than using the current clock time. I also added an animateWhilePaused property to ModelAnimationCollection. When false (the default), we continue to do the short circuit exit from ModelAnimationCollection.update when the scene time hasn't changed. When true, a suitable animationTime function can continue to animate the model, even when scene time is paused. The new sandcastle example is just a clone of Time Dynamic Wheels, rewritten to use Cesium_Man.glb, and the new functionality.
- rename _prevAnimationTime to _prevAnimationDelta - rewrite the _prevAnimationDelta bailout - add a test that we animate while paused if animateWhilePaused is true, and update the existing test to check that we don't animate while paused when animateWhilePaused is false
Use `defined(scheduledAnimation.animationTime)`
@j9liu Once I'm done, should I squash it down to a single commit, or leave it as is? |
@markw65 Leave it as is. Squashing is only important when we need to remove files that were added in the same PR (e.g. the new model and sandcastle). |
@j9liu Ok, I've applied your suggestions, and rebased to fix CHANGES.md. My thought on squashing is that it makes the git history cleaner. |
Merging now -- thanks @markw65 ! |
@j9liu Thank you! |
Cesium currently only supports time based animation. This can be inconvenient if the phase of the animation is related to something other than time (eg distance along a path of an object moving at a variable speed).
This came up before in a pull request here, but the author was persuaded that it was better to use nodeTransformations to explicitly control the model. That was (just) doable with that example, because there were just 3 pairs of wheels, all of which needed the exact same, relatively trivial, transformations.
My model has a bicycle chain with 90+ links, two wheels, two chain rings (with different rates of rotation, which are both different again from the rate of rotation of the wheels), two drive arms, and a rider. Using
nodeTransformations
would be extremely complex, and any changes to the model would require updating all the associated javascript. Also, on the site where I use the model, I actually use different models for different types of activity - so I want to just be able to drop in a new model, and have it "just work" (to make it work, I have to ensure that the animation's "duration" in seconds is equal to the distance in meters the model would move during one iteration of the animation).This adds an
animationTime
property toModelAnimation
. If set, it's used byModelAnimationCollection.update
to determine thelocalAnimationTime
, rather than using the current clock time.I also added a
manualAnimation
property toModelAnimationCollection
so we can still do the short circuit exit fromModelAnimationCollection.update
.The new sandcastle example is just a clone of Time Dynamic Wheels, rewritten to use a more complex model, and the new functionality.